Skip to content

Command Line Tools

wheels docker init

Initialize Docker configuration for your Wheels application with support for multiple databases, CF engines, production mode, and Nginx reverse proxy.

Terminal window
wheels docker init [options]

The wheels docker init command creates Docker configuration files for containerizing your Wheels application. It generates a Dockerfile, docker-compose.yml, .dockerignore, configures datasources in CFConfig.json, and creates a centralized deployment configuration in config/deploy.yml.

The command follows an interactive flow that prompts you for project-specific details, which are then used to populate your configuration files.

OptionDescriptionDefaultValid Values
--dbDatabase system to usemysqlh2, sqlite, mysql, postgres, mssql, oracle
--dbVersionDatabase version to usevaries by dbAny valid version for the selected database
--cfengineCFML engine to useluceelucee, adobe
--cfVersionCFML engine version6Any valid version for the selected engine
--portCustom application port (overrides server.json)from server.json or 8080Any valid port number
--forceOverwrite existing Docker files without confirmationfalsetrue, false
--productionGenerate production-ready configurationfalsetrue, false
--nginxInclude Nginx reverse proxyfalsetrue, false

Default Database Versions:

  • MySQL: 8.0
  • PostgreSQL: 15
  • MSSQL: 2019-latest
  • Oracle: latest (Oracle Database 23c Free)
  • H2: embedded (no version needed)
  • SQLite: embedded (no version needed)
Terminal window
wheels docker init
Terminal window
wheels docker init --db=postgres
Terminal window
wheels docker init --db=postgres --dbVersion=13
Terminal window
wheels docker init --cfengine=adobe --cfVersion=2023
Terminal window
wheels docker init --db=h2

Initialize with SQLite file-based database

Section titled “Initialize with SQLite file-based database”
Terminal window
wheels docker init --db=sqlite
Terminal window
wheels docker init --db=oracle
Terminal window
wheels docker init --db=oracle --dbVersion=23-slim
Terminal window
wheels docker init --port=3000
Terminal window
wheels docker init --force
Terminal window
wheels docker init --production
Terminal window
wheels docker init --production --nginx
Terminal window
wheels docker init --nginx --port=8080
Terminal window
wheels docker init --db=postgres --dbVersion=15 --cfengine=lucee --cfVersion=6 --port=8888 --production --nginx
  1. Interactive Configuration Gathering:

    • Application Name: Prompts for a human-readable name for your app (defaults to folder name).
    • Docker Image Name: Prompts for the repository name to use for builds (defaults to app name).
    • Server Host/IP: Prompts for the production server address for deployments.
    • Server User: Prompts for the SSH user (defaults to ubuntu).
    • These choices are saved to config/deploy.yml and used by all other wheels docker commands.
  2. Checks for existing files (unless --force is used):

    • Detects existing Dockerfile, docker-compose.yml, .dockerignore, and config/deploy.yml
    • Prompts before overwriting existing Docker configuration
    • Lists all files that will be replaced
    • Allows cancellation to prevent accidental overwrites
  3. Creates Dockerfile optimized for CFML applications:

    • Development mode (default):
      • Hot-reload enabled
      • Development tools installed (curl, nano)
      • Source code mounted for live updates
      • BOX_INSTALL=TRUE for automatic dependency installation
    • Production mode (--production):
      • Security hardened with non-root user (UID 1001)
      • box install --production for optimized dependencies
      • Health checks configured (30s interval, 10s timeout, 3 retries)
      • Production environment variables set
      • No source volume mounts
      • Optimized image size
    • Based on ortussolutions/commandbox:latest
    • Installs H2 extension for Lucee if H2 database selected
    • SQLite JDBC driver included by default (no extension needed)
    • Exposes application port (custom, from server.json, or defaults to 8080)
  4. Generates docker-compose.yml with:

    • Application service with port mapping or internal exposure
    • Database service (mysql, postgres, mssql, or oracle) if selected
    • File-based databases (H2, SQLite) run embedded within app container
    • Nginx reverse proxy service (if --nginx)
    • Environment variables for database connection
    • Volume mappings for data persistence
    • Development mode: Source code mounted for hot-reload, development command
    • Production mode: No source mounts, restart: always policies
  5. Creates Nginx configuration (if --nginx):

    • Reverse proxy to application backend
    • Load balancing ready upstream configuration
    • WebSocket support with upgrade headers
    • Static asset caching with 1-day expiration
    • Custom upload size limits (100MB)
    • Health check endpoint at /health
    • Production mode only:
      • Security headers (X-Frame-Options, X-Content-Type-Options, X-XSS-Protection, Referrer-Policy)
      • Gzip compression for text content types
    • Port configuration:
      • Development: Nginx on port 8080
      • Production: Nginx on port 80
  6. Creates .dockerignore excluding:

    • .git and .gitignore
    • node_modules
    • .CommandBox
    • server.json
    • logs and *.log
    • tests
    • .env
    • Production mode only: README files, IDE configs (.vscode, .idea), .DS_Store
  7. Updates CFConfig.json:

    • Configures wheels-dev datasource for selected database
    • Sets up proper JDBC drivers and connection strings:
      • MySQL: com.mysql.cj.jdbc.Driver
      • PostgreSQL: org.postgresql.Driver
      • MSSQL: com.microsoft.sqlserver.jdbc.SQLServerDriver
      • Oracle: oracle.jdbc.OracleDriver
    • Uses Docker service name db for host resolution
    • Configures appropriate ports and credentials
    • Note: Skipped for H2 and SQLite (file-based databases)
  8. Updates server.json for Docker compatibility:

    • Sets web.host to 0.0.0.0 (required for Docker containers to accept external connections)
    • Sets openBrowser to false (Docker containers have no GUI)
    • Configures port from --port argument, existing server.json, or defaults to 8080
    • Sets CFEngine version (e.g., lucee@6 or adobe@2023)
    • Adds CFConfigFile reference if missing
FROM ortussolutions/commandbox:latest
## Install curl and nano
RUN apt-get update && apt-get install -y curl nano
## Clean up the image
RUN apt-get clean && rm -rf /var/lib/apt/lists/*
## Copy application files
COPY . /app
WORKDIR /app
## Install Dependencies
ENV BOX_INSTALL TRUE
## Expose port
EXPOSE 8080
## Set Healthcheck URI
ENV HEALTHCHECK_URI "http://127.0.0.1:8080/"
## Start the application
CMD ["box", "server", "start", "--console", "--force"]
FROM ortussolutions/commandbox:latest
## Install required packages
RUN apt-get update && apt-get install -y curl nano && \
apt-get clean && rm -rf /var/lib/apt/lists/*
## Copy application files
COPY . /app
WORKDIR /app
## Install Dependencies
RUN box install --production
## Production optimizations
ENV ENVIRONMENT production
ENV BOX_SERVER_PROFILE production
## Security: Run as non-root user
RUN useradd -m -u 1001 appuser && \
chown -R appuser:appuser /app
USER appuser
## Expose port
EXPOSE 8080
## Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
CMD curl -f http://127.0.0.1:8080/ || exit 1
## Start the application
CMD ["box", "server", "start", "--console"]

docker-compose.yml (Development with MySQL)

Section titled “docker-compose.yml (Development with MySQL)”
version: "3.8"
services:
app:
build: .
ports:
- "8080:8080"
environment:
ENVIRONMENT: development
DB_HOST: db
DB_PORT: 3306
DB_NAME: wheels
DB_USER: wheels
DB_PASSWORD: wheels
volumes:
- .:/app
- ../../../core/src/wheels:/app/vendor/wheels
- ../../../docs:/app/vendor/wheels/docs
- ../../../tests:/app/tests
command: sh -c "box install && box server start --console --force"
depends_on:
- db
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: wheels
MYSQL_DATABASE: wheels
MYSQL_USER: wheels
MYSQL_PASSWORD: wheels
ports:
- "3306:3306"
volumes:
- db_data:/var/lib/mysql
volumes:
db_data:

docker-compose.yml (Production with Nginx)

Section titled “docker-compose.yml (Production with Nginx)”
version: "3.8"
services:
app:
build: .
expose:
- 8080
environment:
ENVIRONMENT: production
DB_HOST: db
DB_PORT: 3306
DB_NAME: wheels
DB_USER: wheels
DB_PASSWORD: wheels
restart: always
depends_on:
- db
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
restart: always
depends_on:
- app
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: wheels
MYSQL_DATABASE: wheels
MYSQL_USER: wheels
MYSQL_PASSWORD: wheels
ports:
- "3306:3306"
volumes:
- db_data:/var/lib/mysql
volumes:
db_data:

config/deploy.yml (New Centralized Config)

Section titled “config/deploy.yml (New Centralized Config)”
name: myapp
image: myuser/myapp
servers:
- host: 1.2.3.4
user: ubuntu
role: production
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logging
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# Performance
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
upstream app_backend {
server app:8080;
}
server {
listen 80;
server_name _;
# Max upload size
client_max_body_size 100M;
# Security headers (production only)
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
# Gzip compression (production only)
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/x-javascript application/xml+rss application/json;
location / {
proxy_pass http://app_backend;
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;
# WebSocket support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Static assets caching
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
proxy_pass http://app_backend;
proxy_cache_valid 200 1d;
expires 1d;
add_header Cache-Control "public, immutable";
}
# Health check endpoint
location /health {
access_log off;
proxy_pass http://app_backend;
}
}
}
  • Image: mysql:8.0 (default)
  • Port: 3306
  • Credentials: wheels/wheels
  • Database: wheels
  • Root Password: wheels
  • Image: postgres:15 (default)
  • Port: 5432
  • Credentials: wheels/wheels
  • Database: wheels
  • Image: mcr.microsoft.com/mssql/server:2019-latest (default)
  • Port: 1433
  • Credentials: sa/Wheels123!
  • Database: wheels
  • Note: Requires EULA acceptance
  • Image: gvenzl/oracle-free:latest (default - Oracle Database 23c Free)
  • Port: 1521
  • Credentials: wheels/wheels
  • SID: FREE
  • Note: Uses lightweight Oracle Free container image
  • Available tags: latest, 23, 23-slim, 23-faststart, 23-slim-faststart
  • Embedded: No separate container needed
  • Extension: Automatically added to Lucee deployments via Dockerfile
  • Connection: Configured in application settings
  • Storage: Within application container filesystem at ./db/
  • JDBC Driver: org.h2.Driver
  • Embedded: No separate container needed (file-based)
  • JDBC Driver: org.sqlite.JDBC (included with Lucee/CommandBox by default)
  • Connection: Configured in application settings
  • Storage: Within application container filesystem at ./db/
  • Database File: ./db/database_name.db
  • Characteristics:
    • Serverless, zero-configuration
    • Single file database (easy to backup)
    • Ideal for development, testing, and prototyping
    • No username/password required
    • Creates auxiliary files during operation (.db-wal, .db-shm, .db-journal)
  • Use Cases: Development, testing, embedded systems, portable applications
  • Limitations:
    • Single writer (not suitable for high-concurrency)
    • Not recommended for production with multiple concurrent users
  • Source code mounted as volumes for hot-reload
  • Full development tools installed (curl, nano)
  • Debugging and verbose logging enabled
  • No restart policies
  • Direct port exposure (app accessible on configured port)
  • Development-friendly error messages
  • box install runs on container start with --force flag
  • Volume mounts for Wheels core, docs, and tests (framework development)
  • No source code volume mounts - code baked into image
  • Security hardened Dockerfile with non-root user (UID 1001)
  • Automatic restart policies (restart: always)
  • Health checks configured (30s interval, 10s timeout, 60s start period, 3 retries)
  • Optimized image size with box install --production
  • Production environment variable set
  • Additional .dockerignore exclusions (docs, IDE configs)
  • BOX_SERVER_PROFILE=production environment
  • No command override in docker-compose (uses CMD from Dockerfile)
FeatureDevelopmentProduction
Source Volume Mount✅ Yes❌ No
Hot Reload✅ Enabled❌ Disabled
Userrootappuser (1001)
Restart Policynonealways
Health Checks❌ No✅ Yes
Security Headers❌ No✅ Yes (with nginx)
Gzip Compression❌ No✅ Yes (with nginx)
Install Commandbox installbox install --production
Image SizeLargerOptimized

When using --nginx, an Nginx reverse proxy is configured between clients and your application.

  • Load balancing ready: Upstream configuration supports multiple app instances
  • SSL termination point: Add SSL certificates to nginx without app changes
  • Static asset caching: 1-day cache for images, CSS, JS
  • Security headers: Production mode adds security headers
  • Gzip compression: Production mode compresses responses
  • WebSocket support: Upgrade headers configured
  • Request buffering: Better handling of slow clients
  • Health checks: Dedicated /health endpoint
  • Development mode: Nginx listens on port 8080, app exposed internally
  • Production mode: Nginx listens on port 80, app exposed internally
  • App is only accessible through nginx when --nginx is used

The generated nginx.conf includes:

  • Upstream: app_backend pointing to app:8080 (Docker service)
  • Worker connections: 1024 concurrent connections
  • Client max body size: 100MB for file uploads
  • Proxy headers: Host, X-Real-IP, X-Forwarded-For, X-Forwarded-Proto
  • WebSocket support: HTTP/1.1 with Upgrade and Connection headers
  • Timeouts: 60s for connect, send, and read operations
  • Static caching: 1 day expiration for assets
  • Health check: /health endpoint with no access logging

Production Security Headers (with —production —nginx)

Section titled “Production Security Headers (with —production —nginx)”
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Referrer-Policy: no-referrer-when-downgrade

Production Gzip Compression (with —production —nginx)

Section titled “Production Gzip Compression (with —production —nginx)”

Enabled for:

  • text/plain
  • text/css
  • text/xml
  • text/javascript
  • application/x-javascript
  • application/xml+rss
  • application/json

The command automatically updates your server.json with Docker-specific settings:

{
"name": "myapp",
"web": {
"host": "localhost",
"http": {
"port": "8080"
}
},
"openBrowser": true
}
{
"name": "myapp",
"web": {
"host": "0.0.0.0",
"http": {
"enable": true,
"port": "8080"
},
"webroot":"public",
"rewrites":{
"enable":true,
"config":"public/urlrewrite.xml"
}
},
"openBrowser": false,
"CFConfigFile": "CFConfig.json",
"app": {
"cfengine": "lucee@6"
}
}
SettingValueReason
web.host0.0.0.0Docker containers must bind to all interfaces to accept external connections. Using localhost or 127.0.0.1 prevents access from outside the container.
openBrowserfalseDocker containers run in headless mode with no GUI. Attempting to open a browser will fail and cause errors.
web.http.port(from —port or existing)Ensures the application port matches the Dockerfile EXPOSE and docker-compose port mapping.
CFConfigFileCFConfig.jsonRequired for datasource configuration to work properly.

The port configuration follows this priority order:

  1. --port command argument (highest priority)
  2. Existing value in server.json
  3. Default: 8080 (lowest priority)

If you specify --port=9000, the command will:

  • Update server.json with port 9000
  • Configure Dockerfile to EXPOSE 9000
  • Set docker-compose port mapping to 9000:9000

The following environment variables are configured in docker-compose.yml:

VariableDescriptionExample
ENVIRONMENTApplication environment modedevelopment or production
BOX_SERVER_PROFILECommandBox server profile (production only)production
DB_HOSTDatabase hostname (Docker service name)db
DB_PORTDatabase port3306, 5432, 1433, 1521
DB_NAMEDatabase namewheels
DB_USERDatabase usernamewheels or sa
DB_PASSWORDDatabase passwordwheels or Wheels123!
DB_SIDOracle SID (Oracle only)FREE
DB_TYPEDatabase type (H2/SQLite only)h2 or sqlite

After running wheels docker init, start your containers:

Terminal window
# Start in detached mode
docker-compose up -d
# Start with build (after code changes in production)
docker-compose up -d --build
# View logs
docker-compose logs -f
# View specific service logs
docker-compose logs -f app
docker-compose logs -f nginx
# Stop containers
docker-compose down
# Stop and remove volumes (WARNING: deletes database data)
docker-compose down -v
# Restart a specific service
docker-compose restart app
# Rebuild and restart
docker-compose up -d --build --force-recreate
  • Requires Docker and Docker Compose installed
  • Use --force to skip confirmation prompts when overwriting existing files
  • server.json is automatically configured for Docker compatibility:
    • web.host changed to 0.0.0.0 (required for Docker networking)
    • openBrowser set to false (no GUI in containers)
    • web.http.port updated to match —port or existing value
    • CFConfigFile added if missing
    • CF engine version set (e.g., lucee@6)
  • Custom --port overrides the port from server.json
  • Port priority: --port argument > server.json > default (8080)
  • Database passwords are set to defaults suitable for development only
  • Production deployments MUST use secrets management and secure passwords
  • Production mode creates security-hardened configurations with non-root users
  • Nginx adds reverse proxy capabilities for load balancing, caching, and security
  • H2 and SQLite databases run embedded within the application container
  • Volume mounts in development assume Wheels framework development structure
  • When using --nginx, the app is only exposed internally to nginx
  • CFConfig.json is updated with datasource configuration (skipped for H2 and SQLite)

Problem: Port already in use on host machine

Solutions:

  • Update the port in server.json before running wheels docker init
  • Use --port argument to specify a different port
  • Manually edit the ports in docker-compose.yml after generation
  • Stop the conflicting service: lsof -ti:8080 | xargs kill

Problem: Application cannot connect to database

Solutions:

  • Ensure the database container is fully started: docker-compose logs db
  • Check for initialization errors in database logs
  • Verify CFConfig.json has correct datasource configuration
  • Confirm the wheels-dev datasource name matches your application config
  • For MSSQL, ensure password meets complexity requirements
  • Wait for database to fully initialize (can take 30-60 seconds first time)

Problem: Permission denied errors in production mode

Solutions:

  • The generated files have 777 permissions for development convenience
  • Production Dockerfile runs as appuser (UID 1001)
  • Ensure all files are readable by UID 1001
  • Check volume mount permissions on host
  • Adjust file permissions: chmod -R 755 /path/to/app

Problem: 502 Bad Gateway or connection refused

Solutions:

  • Verify app service is running: docker-compose ps
  • Check app service logs: docker-compose logs app
  • Ensure app is listening on the correct port internally
  • Verify nginx config syntax: docker-compose exec nginx nginx -t
  • Check nginx logs: docker-compose logs nginx
  • Restart services: docker-compose restart app nginx

Problem: Still getting prompted despite using --force

Solutions:

  • Ensure correct syntax: wheels docker init --force (not --force=true)
  • Check for boolean parameter issues in command
  • Use the exact parameter name: --force

Problem: H2 extension not loading or database errors

Solutions:

  • Verify H2 extension was added to Dockerfile
  • Check Lucee admin for H2 extension installation
  • Ensure CFConfig.json doesn’t have conflicting datasource config
  • Check application logs for H2 initialization errors
  • H2 works with Lucee only (not Adobe ColdFusion)

Problem: SQLite connection errors or database not found

Solutions:

  • Verify ./db/ directory exists in the container
  • Check file permissions on database file (especially in production mode with UID 1001)
  • Ensure absolute paths are used in datasource configuration
  • SQLite JDBC driver is included by default (no extension installation needed)
  • Check application logs for SQLite initialization errors
  • Verify database file path in application settings
  • In production, ensure database file is accessible by appuser (UID 1001)

Problem: Cannot access the application at http://localhost:8080

Solutions:

  • Verify server.json has web.host set to 0.0.0.0 (not localhost or 127.0.0.1)
  • Check if port is already in use: lsof -ti:8080 (Unix) or netstat -ano | findstr :8080 (Windows)
  • Ensure docker-compose port mapping matches server.json port
  • Check container logs: docker-compose logs app
  • Restart containers: docker-compose restart app
  • If manually edited, run wheels docker init --force to regenerate proper configuration

Problem: Errors about browser opening or display issues

Solutions:

  • Verify server.json has openBrowser: false
  • Run wheels docker init --force to update server.json automatically
  • Manually set "openBrowser": false in server.json
  • Rebuild containers: docker-compose up -d --build