Docker Deployment
Overview
Vulcan provides production-ready Docker images for easy deployment. The images are optimized for size and performance, using multi-stage builds and memory-efficient configurations.
Quick Start
# Clone or download the repository
git clone https://github.com/mitre/vulcan.git
cd vulcan
# Generate secrets (one time)
./setup-docker-secrets.sh
# Start Vulcan
docker compose up
# Open http://localhost:3000
# Register your first user - they become admin automaticallyWhat happens on first start:
- PostgreSQL starts with generated credentials
- The entrypoint runs
db:prepare(creates database, runs migrations) - Admin bootstrap runs automatically (hooked into
db:prepare) - First user to register becomes admin (via
VULCAN_FIRST_USER_ADMIN=true)
Admin Bootstrap Options
Vulcan provides three ways to create the initial admin:
First User Admin (Default): First user to register becomes admin
- Enabled by default via
VULCAN_FIRST_USER_ADMIN=true - Protected by PostgreSQL advisory lock against race conditions
- Enabled by default via
Environment Variables (Recommended for Production):
bash# Add to .env VULCAN_ADMIN_EMAIL=admin@example.com VULCAN_ADMIN_PASSWORD=SecurePassword123! VULCAN_FIRST_USER_ADMIN=false- Admin created automatically during
db:prepare - If password omitted, a secure random password is generated and logged
- Admin created automatically during
Manual Rake Task:
bashdocker compose exec web rails db:create_admin
Note: For local testing without SSL/reverse proxy, add
RAILS_FORCE_SSL=falseto your.envfile. For production with SSL termination (nginx, traefik), keep the defaultRAILS_FORCE_SSL=true.
Using Docker Run
# Pull the latest image
docker pull mitre/vulcan:latest
# Run with PostgreSQL
docker run -d \
--name vulcan \
-p 3000:3000 \
-e DATABASE_URL="postgresql://user:pass@host/vulcan" \
-e SECRET_KEY_BASE="your-secret-key" \
mitre/vulcan:latestImage Details
- Base: Ruby 3.4.9 on Debian Bookworm
- Architectures: linux/amd64, linux/arm64 (multi-arch, built natively via Docker Build Cloud)
- Size: ~1.76GB (73% smaller than v2.1)
- Memory: Uses jemalloc for 20-40% memory reduction
- Security: Non-root user, minimal attack surface
Configuration
Environment Variables
See Environment Variables for complete list.
Key variables for Docker:
DATABASE_URL- PostgreSQL connection stringSECRET_KEY_BASE- Rails secret keyRAILS_ENV- Set toproductionRAILS_LOG_TO_STDOUT- Set totruefor container logsVULCAN_SESSION_TIMEOUT- Session inactivity timeout (e.g.,15m,900,1h). DoD standard:15m
Volumes
volumes:
- ./data/postgres:/var/lib/postgresql/data
- ./data/uploads:/app/public/uploads
- ./certs:/rails/certs # For corporate SSL certificatesProduction Deployment
1. Generate Secrets
./setup-docker-secrets.sh
# Choose option 2 for production2. Configure Authentication
Edit .env file to set up OIDC/LDAP:
# OIDC Configuration
VULCAN_ENABLE_OIDC=true
VULCAN_OIDC_ISSUER_URL=https://your-domain.okta.com
VULCAN_OIDC_CLIENT_ID=your-client-id
VULCAN_OIDC_CLIENT_SECRET=your-client-secret
# Or LDAP Configuration
VULCAN_ENABLE_LDAP=true
VULCAN_LDAP_HOST=ldap.example.com
VULCAN_LDAP_PORT=636
VULCAN_LDAP_BASE=dc=example,dc=com3. SSL/TLS Setup
For HTTPS, use a reverse proxy like nginx. The proxy should set the X-Forwarded-Proto header so Rails knows the original request was HTTPS.
Important: Keep
RAILS_FORCE_SSL=true(default) when using a reverse proxy. Only setRAILS_FORCE_SSL=falsefor local Docker testing without SSL termination.
Example nginx configuration:
server {
listen 443 ssl;
server_name vulcan.example.com;
ssl_certificate /etc/ssl/certs/vulcan.crt;
ssl_certificate_key /etc/ssl/private/vulcan.key;
location / {
proxy_pass http://localhost:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}4. Database Initialization
The Docker entrypoint automatically runs db:prepare on every container start:
- First startup: Creates the database, loads
db/schema.rb, runs seeds, bootstraps admin - Subsequent startups: Runs pending migrations only (existing data is preserved)
No manual database initialization is needed. To run migrations manually:
docker compose run --rm web bundle exec rails db:migrateWhy
db:prepareand notdb:migrate?db:preparehandles both fresh and existing databases. It is the Rails 8 standard pattern for Docker entrypoints. Unlikedb:schema:load,db:preparedoes NOT needDISABLE_DATABASE_ENVIRONMENT_CHECK— it callsload_schema()as a Ruby method, bypassing the protected environment check by design. See Rails source: DatabaseTasks#initialize_database for details.
Monitoring
Health Check Endpoints
Vulcan provides health check endpoints for container orchestration and monitoring:
| Endpoint | Purpose | Response |
|---|---|---|
GET /up | Liveness probe (is process alive?) | HTML with green background |
GET /health_check | Readiness probe (database connected?) | ok or service unavailable |
GET /health_check/database | Database connectivity only | ok or service unavailable |
GET /health_check/migrations | Pending migrations check | ok or service unavailable |
Docker Compose health check (built into image):
# Check container health status
docker inspect --format='{{.State.Health.Status}}' vulcan
# Manual endpoint test
curl http://localhost:3000/up
curl http://localhost:3000/health_checkKubernetes probe configuration:
livenessProbe:
httpGet:
path: /up
port: 3000
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /health_check
port: 3000
initialDelaySeconds: 15
periodSeconds: 5Logs
# View logs
docker compose logs -f web
# Or for single container
docker logs -f vulcanBackup and Restore
Database Backup
# Backup
docker compose exec db pg_dump -U postgres vulcan_vue_production > backup.sql
# Restore
docker compose exec -T db psql -U postgres vulcan_vue_production < backup.sqlApplication Data
# Backup uploads
tar -czf uploads-backup.tar.gz ./data/uploads
# Restore uploads
tar -xzf uploads-backup.tar.gzTroubleshooting
Common Issues
Database connection failed
- Ensure PostgreSQL is running
- Check DATABASE_URL format
- Verify network connectivity
Asset compilation errors
- Run:
docker compose run --rm web bundle exec rails assets:precompile
- Run:
Permission errors
- Check volume mount permissions
- Ensure UID/GID match between host and container
Debug Mode
# Run with debug output
docker compose run --rm -e RAILS_LOG_LEVEL=debug webSecurity Considerations
- Always use strong SECRET_KEY_BASE
- Enable HTTPS in production
- Configure authentication (OIDC/LDAP)
- Regularly update base images
- Use read-only root filesystem where possible
- Implement network segmentation
Next Steps
- Kubernetes Deployment - For orchestrated deployments
- Authentication Setup - Configure SSO
- Environment Variables - Full configuration reference
