Skip to content

Heroku Deployment ​

This guide covers deploying Vulcan to Heroku, a platform-as-a-service (PaaS) that enables developers to build and run applications entirely in the cloud.

Prerequisites ​

  • Heroku account (free tier available)
  • Heroku CLI installed locally
  • Git repository with Vulcan code
  • PostgreSQL add-on (Heroku Postgres)

Quick Deploy ​

Deploy with Heroku Button ​

Deploy to Heroku

Manual Deployment ​

  1. Create a new Heroku app:
bash
heroku create your-vulcan-app
  1. Add PostgreSQL database:
bash
heroku addons:create heroku-postgresql:mini
  1. Set required environment variables:
bash
# Ruby buildpack configuration
heroku config:set RAILS_ENV=production
heroku config:set RAILS_SERVE_STATIC_FILES=true
heroku config:set RAILS_LOG_TO_STDOUT=true

# Application configuration
heroku config:set SECRET_KEY_BASE=$(rails secret)
heroku config:set VULCAN_CONTACT_EMAIL=admin@example.com
heroku config:set VULCAN_APP_URL=https://your-vulcan-app.herokuapp.com
  1. Deploy the application:
bash
git push heroku main

Migrations run automatically via the Procfile release phase (bundle exec rails db:migrate). No manual migration step is needed.

  1. Create admin user (choose one):

    Option A — First-user-admin (simplest):

    bash
    heroku config:set VULCAN_FIRST_USER_ADMIN=true

    Then register via the web UI — the first user becomes admin.

    Option B — Environment variable bootstrap:

    bash
    heroku config:set VULCAN_ADMIN_EMAIL=admin@example.com
    heroku config:set VULCAN_ADMIN_PASSWORD=SecurePassword123!

    Admin is created on the next deploy (hooked into db:prepare via Docker, or run manually with heroku run rails admin:bootstrap).

Configuration ​

Essential Environment Variables ​

bash
# Database (automatically set by Heroku Postgres)
DATABASE_URL=postgres://...

# Rails configuration
SECRET_KEY_BASE=your-secret-key
RAILS_ENV=production
RAILS_SERVE_STATIC_FILES=true
RAILS_LOG_TO_STDOUT=true

# Vulcan configuration
VULCAN_CONTACT_EMAIL=admin@example.com
VULCAN_APP_URL=https://your-app.herokuapp.com
VULCAN_WELCOME_TEXT="Welcome to Vulcan"

# Email configuration (Community Best Practice)
# Simple setup: Most deployments only need VULCAN_CONTACT_EMAIL
# Following Rails community standard like OpenProject/Alonetone
VULCAN_CONTACT_EMAIL=support@yourcompany.com

# Recommended: Use professional email service (avoid Gmail SMTP)
# Option 1: Mailgun (Free tier: 100 emails/day)
heroku addons:create mailgun:starter
VULCAN_ENABLE_SMTP=true
VULCAN_SMTP_ADDRESS=smtp.mailgun.org
VULCAN_SMTP_PORT=587
VULCAN_SMTP_AUTHENTICATION=plain
# VULCAN_SMTP_SERVER_USERNAME automatically defaults to VULCAN_CONTACT_EMAIL
VULCAN_SMTP_SERVER_PASSWORD=your-mailgun-password
VULCAN_SMTP_DOMAIN=your-app.herokuapp.com
VULCAN_SMTP_ENABLE_STARTTLS_AUTO=true

# Option 2: SendGrid (if you need different SMTP username)
VULCAN_ENABLE_SMTP=true
VULCAN_SMTP_ADDRESS=smtp.sendgrid.net
VULCAN_SMTP_PORT=587
VULCAN_SMTP_AUTHENTICATION=plain
VULCAN_SMTP_SERVER_USERNAME=apikey  # Override when different from contact_email
VULCAN_SMTP_SERVER_PASSWORD=your-sendgrid-api-key
VULCAN_SMTP_DOMAIN=heroku.com
VULCAN_SMTP_ENABLE_STARTTLS_AUTO=true

For DoD/government deployments, enable the classification banner and consent modal:

bash
# Classification banner (top + bottom of every page)
heroku config:set VULCAN_BANNER_ENABLED=true
heroku config:set VULCAN_BANNER_TEXT=UNCLASSIFIED
heroku config:set VULCAN_BANNER_BACKGROUND_COLOR=#007a33
heroku config:set VULCAN_BANNER_TEXT_COLOR=#ffffff

# Consent/terms-of-use modal (blocks access until acknowledged)
heroku config:set VULCAN_CONSENT_ENABLED=true
heroku config:set VULCAN_CONSENT_VERSION=1
heroku config:set VULCAN_CONSENT_TITLE="Acceptable Use Policy"
heroku config:set VULCAN_CONSENT_CONTENT="By using this system you agree to the **acceptable use policy**."
heroku config:set VULCAN_CONSENT_TTL=0

Consent acknowledgment is tracked server-side in the Rails session (AC-8 compliant). VULCAN_CONSENT_TTL=0 means consent is required every session (DoD default). Set to 24h or 12h for less strict environments. Consent content supports Markdown. Increment VULCAN_CONSENT_VERSION to re-prompt all users after policy changes.

See Configuration for the full DoD color table.

Authentication Providers ​

For OAuth/OIDC authentication, add:

bash
# GitHub OAuth
VULCAN_GITHUB_APP_ID=your-github-app-id
VULCAN_GITHUB_APP_SECRET=your-github-app-secret

# OIDC
VULCAN_ENABLE_OIDC=true
VULCAN_OIDC_ISSUER_URL=https://your-provider.com
VULCAN_OIDC_CLIENT_ID=your-client-id
VULCAN_OIDC_CLIENT_SECRET=your-client-secret

Add-ons ​

  1. Heroku Postgres (Required)

    bash
    heroku addons:create heroku-postgresql:mini
  2. SendGrid (Email service)

    bash
    heroku addons:create sendgrid:starter
  3. Papertrail (Logging)

    bash
    heroku addons:create papertrail:choklad
  4. New Relic (Application monitoring)

    bash
    heroku addons:create newrelic:wayne
  5. Heroku Scheduler (Background jobs)

    bash
    heroku addons:create scheduler:standard

Scaling ​

Dyno Configuration ​

bash
# Scale web dynos
heroku ps:scale web=1

# For production workloads
heroku ps:scale web=2:standard-2x

# Add worker dynos if needed
heroku ps:scale worker=1:standard-1x

Database Scaling ​

bash
# Upgrade database plan
heroku addons:upgrade heroku-postgresql:standard-0

Maintenance ​

Updates and Migrations ​

Migrations run automatically during the Procfile release phase on every deploy. Manual migration is rarely needed.

bash
# Deploy updates (migrations run automatically)
git push heroku main

# Manual migration (only if needed)
heroku run rails db:migrate

# For large migrations, enable maintenance mode first
heroku maintenance:on
git push heroku main
heroku maintenance:off

Backup and Restore ​

bash
# Create backup
heroku pg:backups:capture

# List backups
heroku pg:backups

# Download backup
heroku pg:backups:download

# Restore from backup
heroku pg:backups:restore b001 DATABASE_URL

Logs ​

bash
# View recent logs
heroku logs --tail

# View specific process logs
heroku logs --source app --tail

# Export logs to file
heroku logs -n 1500 > production.log

Review Apps ​

Review Apps create a temporary Heroku app for each pull request, with its own fresh database.

How It Works ​

The app.json in the repository root configures review apps:

PhaseCommandWhenPurpose
Releasedb:migrate (Procfile)Every deployRuns pending migrations (no-op on first deploy)
Postdeploydb:schema:load db:seed admin:bootstrapFirst deploy onlyLoads schema, seeds data, creates admin

The postdeploy uses DISABLE_DATABASE_ENVIRONMENT_CHECK=1 because review apps run with RAILS_ENV=production and db:schema:load (the rake task) checks for protected environments. This flag is safe here — review apps always have fresh, empty databases.

Important: DISABLE_DATABASE_ENVIRONMENT_CHECK must NEVER be set for production or staging. It only appears in the review app postdeploy script. See spec/config/database_safety_spec.rb for regression tests enforcing this.

Database Strategy ​

EnvironmentDB CommandSafety
Productiondb:migrate (Procfile release)Only runs pending migrations, never destructive
Stagingdb:migrate (Procfile release)Same as production
Review Appsdb:schema:load (postdeploy, once)Fresh DB only, flag scoped to one-time script
Dockerdb:prepare (entrypoint)Creates if missing, migrates if exists, no flag needed

Setup ​

  1. The app.json is already configured in the repository root
  2. Enable Review Apps in your Heroku Pipeline settings
  3. Each PR will automatically get a review app with:
    • Fresh PostgreSQL database (Essential-0 plan)
    • Schema loaded from db/schema.rb
    • First-user-admin enabled (VULCAN_FIRST_USER_ADMIN=true)

Troubleshooting ​

Common Issues ​

  1. Assets not loading

    bash
    heroku config:set RAILS_SERVE_STATIC_FILES=true
    heroku run rails assets:precompile
  2. Database connection errors

    bash
    # Check database URL
    heroku config:get DATABASE_URL
    
    # Restart database
    heroku pg:restart
  3. Memory issues

    bash
    # Check memory usage
    heroku ps
    
    # Scale to larger dyno
    heroku ps:resize web=standard-2x
  4. Deployment failures

    bash
    # Check build log
    heroku builds:info
    
    # Clear build cache
    heroku builds:cache:purge

Security Considerations ​

  1. Use strong SECRET_KEY_BASE

    bash
    heroku config:set SECRET_KEY_BASE=$(openssl rand -hex 64)
  2. Enable HTTPS only

    • Heroku provides SSL certificates automatically
    • Force SSL in Rails configuration
  3. Restrict database access

    • Use Heroku Private Spaces for sensitive data
    • Enable database encryption at rest
  4. Regular updates

    bash
    # Update Ruby buildpack
    heroku buildpacks:set heroku/ruby
    
    # Update dependencies
    bundle update --conservative

Cost Optimization ​

  • Use Eco dynos for development/staging ($5/month)
  • Schedule dyno sleeping for non-production apps
  • Use Heroku Postgres Mini for small projects ($5/month)
  • Monitor usage with heroku ps:type

Additional Resources ​

Part of the MITRE Security Automation Framework (SAF)