wheels docker init
Initialize Docker configuration for your Wheels application with support for multiple databases, CF engines, production mode, and Nginx reverse proxy.
Synopsis
wheels docker init [options]Description
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 optionally creates Nginx configuration for reverse proxy support.
Options
--db
Database system to use
mysql
h2, mysql, postgres, mssql, oracle
--dbVersion
Database version to use
varies by db
Any valid version for the selected database
--cfengine
CFML engine to use
lucee
lucee, adobe
--cfVersion
CFML engine version
6
Any valid version for the selected engine
--port
Custom application port (overrides server.json)
from server.json or 8080
Any valid port number
--force
Overwrite existing Docker files without confirmation
false
true, false
--production
Generate production-ready configuration
false
true, false
--nginx
Include Nginx reverse proxy
false
true, false
Default Database Versions:
MySQL:
8.0PostgreSQL:
15MSSQL:
2019-latestOracle:
latest(Oracle Database 23c Free)H2: embedded (no version needed)
Examples
Basic initialization (MySQL with Lucee 6)
wheels docker initInitialize with PostgreSQL
wheels docker init --db=postgresInitialize with specific database version
wheels docker init --db=postgres --dbVersion=13Initialize with Adobe ColdFusion
wheels docker init --cfengine=adobe --cfVersion=2023Initialize with H2 embedded database
wheels docker init --db=h2Initialize with Oracle
wheels docker init --db=oracleInitialize with specific Oracle version
wheels docker init --db=oracle --dbVersion=23-slimCustom port
wheels docker init --port=3000Force overwrite existing files
wheels docker init --forceProduction configuration
wheels docker init --productionProduction with Nginx reverse proxy
wheels docker init --production --nginxDevelopment with Nginx on custom port
wheels docker init --nginx --port=8080Full custom configuration
wheels docker init --db=postgres --dbVersion=15 --cfengine=lucee --cfVersion=6 --port=8888 --production --nginxWhat It Does
Checks for existing files (unless
--forceis used):Detects existing
Dockerfile,docker-compose.yml, and.dockerignorePrompts before overwriting existing Docker configuration
Lists all files that will be replaced
Allows cancellation to prevent accidental overwrites
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=TRUEfor automatic dependency installation
Production mode (
--production):Security hardened with non-root user (UID 1001)
box install --productionfor optimized dependenciesHealth checks configured (30s interval, 10s timeout, 3 retries)
Production environment variables set
No source volume mounts
Optimized image size
Based on
ortussolutions/commandbox:latestInstalls H2 extension for Lucee if H2 database selected
Exposes application port (custom, from server.json, or defaults to 8080)
Generates docker-compose.yml with:
Application service with port mapping or internal exposure
Database service (mysql, postgres, or mssql) if selected
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: alwayspolicies
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
/healthProduction 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
Creates .dockerignore excluding:
.gitand.gitignorenode_modules.CommandBoxserver.jsonlogsand*.logtests.envProduction mode only: README files, IDE configs (
.vscode,.idea),.DS_Store
Updates CFConfig.json:
Configures
wheels-devdatasource for selected databaseSets up proper JDBC drivers and connection strings:
MySQL:
com.mysql.cj.jdbc.DriverPostgreSQL:
org.postgresql.DriverMSSQL:
com.microsoft.sqlserver.jdbc.SQLServerDriverOracle:
oracle.jdbc.OracleDriver
Uses Docker service name
dbfor host resolutionConfigures appropriate ports and credentials
Updates server.json for Docker compatibility:
Sets
web.hostto0.0.0.0(required for Docker containers to accept external connections)Sets
openBrowsertofalse(Docker containers have no GUI)Configures port from
--portargument, existing server.json, or defaults to 8080Sets CFEngine version (e.g.,
lucee@6oradobe@2023)Adds
CFConfigFilereference if missing
Generated Files
Development Dockerfile
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"]Production Dockerfile
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)
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)
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:nginx.conf (Generated with --nginx)
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;
}
}
}Database Configurations
MySQL
Image:
mysql:8.0(default)Port: 3306
Credentials: wheels/wheels
Database: wheels
Root Password: wheels
PostgreSQL
Image:
postgres:15(default)Port: 5432
Credentials: wheels/wheels
Database: wheels
MSSQL
Image:
mcr.microsoft.com/mssql/server:2019-latest(default)Port: 1433
Credentials: sa/Wheels123!
Database: wheels
Note: Requires EULA acceptance
Oracle
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
H2
Embedded: No separate container needed
Extension: Automatically added to Lucee deployments via Dockerfile
Connection: Configured in CFConfig.json
Storage: Within application container filesystem
Production vs Development Mode
Development Mode (default)
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 installruns on container start with--forceflagVolume mounts for Wheels core, docs, and tests (framework development)
Production Mode (--production)
--production)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 --productionProduction environment variable set
Additional .dockerignore exclusions (docs, IDE configs)
BOX_SERVER_PROFILE=productionenvironmentNo command override in docker-compose (uses CMD from Dockerfile)
Comparison Table
Source Volume Mount
✅ Yes
❌ No
Hot Reload
✅ Enabled
❌ Disabled
User
root
appuser (1001)
Restart Policy
none
always
Health Checks
❌ No
✅ Yes
Security Headers
❌ No
✅ Yes (with nginx)
Gzip Compression
❌ No
✅ Yes (with nginx)
Install Command
box install
box install --production
Image Size
Larger
Optimized
Nginx Reverse Proxy
When using --nginx, an Nginx reverse proxy is configured between clients and your application.
Benefits
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
/healthendpoint
Port Configuration
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
--nginxis used
Nginx Configuration Details
The generated nginx.conf includes:
Upstream:
app_backendpointing toapp: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:
/healthendpoint with no access logging
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-downgradeProduction Gzip Compression (with --production --nginx)
Enabled for:
text/plain
text/css
text/xml
text/javascript
application/x-javascript
application/xml+rss
application/json
server.json Docker Configuration
The command automatically updates your server.json with Docker-specific settings:
Before Docker Init
{
"name": "myapp",
"web": {
"host": "localhost",
"http": {
"port": "8080"
}
},
"openBrowser": true
}After Docker Init
{
"name": "myapp",
"web": {
"host": "0.0.0.0",
"http": {
"port": "8080"
}
},
"openBrowser": false,
"CFConfigFile": "CFConfig.json",
"app": {
"cfengine": "lucee@6"
}
}Why These Changes?
web.host
0.0.0.0
Docker containers must bind to all interfaces to accept external connections. Using localhost or 127.0.0.1 prevents access from outside the container.
openBrowser
false
Docker 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.
CFConfigFile
CFConfig.json
Required for datasource configuration to work properly.
Port Priority
The port configuration follows this priority order:
--portcommand argument (highest priority)Existing value in
server.jsonDefault:
8080(lowest priority)
If you specify --port=9000, the command will:
Update
server.jsonwith port9000Configure Dockerfile to
EXPOSE 9000Set docker-compose port mapping to
9000:9000
Environment Variables
The following environment variables are configured in docker-compose.yml:
ENVIRONMENT
Application environment mode
development or production
BOX_SERVER_PROFILE
CommandBox server profile (production only)
production
DB_HOST
Database hostname (Docker service name)
db
DB_PORT
Database port
3306, 5432, 1433, 1521
DB_NAME
Database name
wheels
DB_USER
Database username
wheels or sa
DB_PASSWORD
Database password
wheels or Wheels123!
DB_SID
Oracle SID (Oracle only)
FREE
DB_TYPE
Database type (H2 only)
h2
Starting Your Docker Environment
After running wheels docker init, start your containers:
# 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-recreateNotes
Requires Docker and Docker Compose installed
Use
--forceto skip confirmation prompts when overwriting existing filesserver.json is automatically configured for Docker compatibility:
web.hostchanged to0.0.0.0(required for Docker networking)openBrowserset tofalse(no GUI in containers)web.http.portupdated to match --port or existing valueCFConfigFileadded if missingCF engine version set (e.g.,
lucee@6)
Custom
--portoverrides the port fromserver.jsonPort priority:
--portargument >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 database runs embedded within the application container
Volume mounts in development assume Wheels framework development structure
When using
--nginx, the app is only exposed internally to nginxCFConfig.json is updated with datasource configuration (skipped for H2)
Troubleshooting
Port conflicts
Problem: Port already in use on host machine
Solutions:
Update the port in
server.jsonbefore runningwheels docker initUse
--portargument to specify a different portManually edit the ports in
docker-compose.ymlafter generationStop the conflicting service:
lsof -ti:8080 | xargs kill
Database connection issues
Problem: Application cannot connect to database
Solutions:
Ensure the database container is fully started:
docker-compose logs dbCheck for initialization errors in database logs
Verify CFConfig.json has correct datasource configuration
Confirm the
wheels-devdatasource name matches your application configFor MSSQL, ensure password meets complexity requirements
Wait for database to fully initialize (can take 30-60 seconds first time)
Permission issues
Problem: Permission denied errors in production mode
Solutions:
The generated files have
777permissions for development convenienceProduction 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
Nginx not routing requests
Problem: 502 Bad Gateway or connection refused
Solutions:
Verify app service is running:
docker-compose psCheck app service logs:
docker-compose logs appEnsure app is listening on the correct port internally
Verify nginx config syntax:
docker-compose exec nginx nginx -tCheck nginx logs:
docker-compose logs nginxRestart services:
docker-compose restart app nginx
Force overwrite not working
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
H2 database not working
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)
Container not accessible from host
Problem: Cannot access the application at http://localhost:8080
Solutions:
Verify
server.jsonhasweb.hostset to0.0.0.0(notlocalhostor127.0.0.1)Check if port is already in use:
lsof -ti:8080(Unix) ornetstat -ano | findstr :8080(Windows)Ensure docker-compose port mapping matches server.json port
Check container logs:
docker-compose logs appRestart containers:
docker-compose restart appIf manually edited, run
wheels docker init --forceto regenerate proper configuration
Application attempts to open browser
Problem: Errors about browser opening or display issues
Solutions:
Verify
server.jsonhasopenBrowser: falseRun
wheels docker init --forceto update server.json automaticallyManually set
"openBrowser": falsein server.jsonRebuild containers:
docker-compose up -d --build
See Also
wheels docker deploy - Deploy using Docker
wheels deploy - General deployment commands
CommandBox Docker Images - Official CommandBox images
Docker Compose Documentation - Docker Compose reference
Nginx Documentation - Nginx configuration reference
Last updated
Was this helpful?

