Skip to content

Docker Compose Deployment

Overview

Three Docker Compose profiles support different deployment scenarios:

ProfileUse CaseServicesFeatures
prodProductionflow8, mongo, mongo-expressMinimal, optimized
devDevelopmentflow8, mongo, mongo-expressHot reload, debugging
localLocal debuggingflow8, mongo, postgres, mongo-expressDelve debugger, all features

Production Profile (docker-compose.yml)

version: '3.8'
services:
flow8:
image: flow8core:latest
container_name: flow8
restart: unless-stopped
ports:
- "4454:4454"
- "4445:4445"
- "7701-7799:7701-7799"
environment:
MONGODB_URI: mongodb://mongo:27017/flow8
MONGODB_DB: flow8
SERVER_PORT: 4454
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
ENCRYPTION_KEY_SALT: ${ENCRYPTION_KEY_SALT}
OAUTH2_CLIENT_ID: ${OAUTH2_CLIENT_ID}
OAUTH2_CLIENT_SECRET: ${OAUTH2_CLIENT_SECRET}
LOG_LEVEL: info
SESSION_COOKIE_SECURE: "true"
SESSION_COOKIE_HTTP_ONLY: "true"
volumes:
- flow8-data:/app/data
depends_on:
mongo:
condition: service_healthy
networks:
- backend
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:4454/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
restart: unless-stopped
resources:
limits:
cpus: "2"
memory: 2G
reservations:
cpus: "1"
memory: 1G
mongo:
image: mongo:6.0
container_name: flow8-mongo
restart: unless-stopped
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ROOT_PASSWORD}
MONGO_INITDB_DATABASE: flow8
volumes:
- mongo-data:/data/db
- mongo-config:/data/configdb
- ./init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js
networks:
- backend
healthcheck:
test: mongosh --eval "db.adminCommand('ping')"
interval: 10s
timeout: 5s
retries: 5
resources:
limits:
cpus: "2"
memory: 2G
mongo-express:
image: mongo-express:latest
container_name: flow8-mongo-express
restart: unless-stopped
ports:
- "8081:8081"
environment:
ME_CONFIG_MONGODB_URL: mongodb://admin:${MONGO_ROOT_PASSWORD}@mongo:27017/
ME_CONFIG_MONGODB_ADMINUSERNAME: admin
ME_CONFIG_MONGODB_ADMINPASSWORD: ${MONGO_ROOT_PASSWORD}
depends_on:
- mongo
networks:
- backend
volumes:
flow8-data:
driver: local
mongo-data:
driver: local
mongo-config:
driver: local
networks:
backend:
driver: bridge

Development Profile (docker-compose.dev.yml)

version: '3.8'
services:
flow8:
build:
context: .
dockerfile: Dockerfile.dev
container_name: flow8-dev
restart: on-failure
ports:
- "4454:4454"
- "4445:4445"
- "7701-7799:7701-7799"
environment:
MONGODB_URI: mongodb://mongo:27017/flow8
SERVER_PORT: 4454
ENCRYPTION_KEY: ${ENCRYPTION_KEY:?Required — generate with: openssl rand -hex 128}
ENCRYPTION_KEY_SALT: ${ENCRYPTION_KEY_SALT:?Required — generate with: openssl rand -hex 32}
LOG_LEVEL: debug
volumes:
- .:/app/src
- flow8-data:/app/data
depends_on:
mongo:
condition: service_healthy
networks:
- backend
environment:
CGO_ENABLED: 1
GOOS: linux
mongo:
image: mongo:6.0
container_name: flow8-mongo-dev
restart: unless-stopped
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: <your-mongo-password>
MONGO_INITDB_DATABASE: flow8
volumes:
- mongo-data:/data/db
- ./init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js
ports:
- "27017:27017"
networks:
- backend
healthcheck:
test: mongosh --eval "db.adminCommand('ping')"
interval: 10s
timeout: 5s
retries: 5
mongo-express:
image: mongo-express:latest
container_name: flow8-mongo-express-dev
restart: unless-stopped
ports:
- "8081:8081"
environment:
ME_CONFIG_MONGODB_URL: mongodb://admin:<your-mongo-password>@mongo:27017/
ME_CONFIG_MONGODB_ADMINUSERNAME: admin
ME_CONFIG_MONGODB_ADMINPASSWORD: <your-mongo-password>
depends_on:
- mongo
networks:
- backend
volumes:
flow8-data:
driver: local
mongo-data:
driver: local
networks:
backend:
driver: bridge

Local Debug Profile (docker-compose.local.yml)

version: '3.8'
services:
flow8:
build:
context: .
dockerfile: Dockerfile.local
container_name: flow8-local
restart: on-failure
ports:
- "4454:4454"
- "4445:4445"
- "7701-7799:7701-7799"
- "2345:2345" # Delve debugger port
environment:
MONGODB_URI: mongodb://mongo:27017/flow8
POSTGRESQL_URI: postgresql://user:password@postgres:5432/flow8_test
SERVER_PORT: 4454
ENCRYPTION_KEY: ${ENCRYPTION_KEY:?Required — generate with: openssl rand -hex 128}
ENCRYPTION_KEY_SALT: ${ENCRYPTION_KEY_SALT:?Required — generate with: openssl rand -hex 32}
LOG_LEVEL: debug
volumes:
- .:/app/src
- flow8-data:/app/data
depends_on:
mongo:
condition: service_healthy
postgres:
condition: service_healthy
networks:
- backend
cap_add:
- SYS_PTRACE # Required for Delve debugger
security_opt:
- "apparmor=unconfined"
mongo:
image: mongo:6.0
container_name: flow8-mongo-local
restart: unless-stopped
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: <your-mongo-password>
MONGO_INITDB_DATABASE: flow8
volumes:
- mongo-data:/data/db
- ./init-mongo.js:/docker-entrypoint-initdb.d/init-mongo.js
ports:
- "27017:27017"
networks:
- backend
healthcheck:
test: mongosh --eval "db.adminCommand('ping')"
interval: 10s
timeout: 5s
retries: 5
postgres:
image: postgres:15
container_name: flow8-postgres-local
restart: unless-stopped
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: password
POSTGRES_DB: flow8_test
volumes:
- postgres-data:/var/lib/postgresql/data
ports:
- "5432:5432"
networks:
- backend
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user"]
interval: 10s
timeout: 5s
retries: 5
mongo-express:
image: mongo-express:latest
container_name: flow8-mongo-express-local
restart: unless-stopped
ports:
- "8081:8081"
environment:
ME_CONFIG_MONGODB_URL: mongodb://admin:<your-mongo-password>@mongo:27017/
ME_CONFIG_MONGODB_ADMINUSERNAME: admin
ME_CONFIG_MONGODB_ADMINPASSWORD: <your-mongo-password>
depends_on:
- mongo
networks:
- backend
volumes:
flow8-data:
driver: local
mongo-data:
driver: local
postgres-data:
driver: local
networks:
backend:
driver: bridge

Environment Configuration

.env File

Terminal window
# MongoDB
MONGO_ROOT_PASSWORD=your_secure_password_here
# Encryption (REQUIRED for production)
ENCRYPTION_KEY=<output of: openssl rand -hex 128>
ENCRYPTION_KEY_SALT=<output of: openssl rand -hex 32>
# OAuth2 (optional)
OAUTH2_CLIENT_ID=your-client-id
OAUTH2_CLIENT_SECRET=your-client-secret
# Database (for local dev)
POSTGRES_USER=user
POSTGRES_PASSWORD=password

.env.example

Terminal window
# Copy to .env and fill in values
MONGO_ROOT_PASSWORD=change_me
ENCRYPTION_KEY=generate_with_openssl_rand_-hex_128
ENCRYPTION_KEY_SALT=generate_with_openssl_rand_-hex_32
OAUTH2_CLIENT_ID=from_azure_ad
OAUTH2_CLIENT_SECRET=from_azure_ad

MongoDB Initialization

init-mongo.js

docker-entrypoint-initdb.d/init-mongo.js
// Runs automatically when MongoDB starts for first time
db = db.getSiblingDB('flow8');
// Create collections with indexes
db.createCollection('flows');
db.flows.createIndex({ company_id: 1, created_at: -1 });
db.flows.createIndex({ company_id: 1, name: 1 });
db.createCollection('plays');
db.plays.createIndex({ company_id: 1, created_at: -1 });
db.plays.createIndex({ flow_id: 1, created_at: -1 });
db.createCollection('audit_logs');
db.audit_logs.createIndex({ company_id: 1, timestamp: -1 });
db.audit_logs.createIndex({ user_id: 1, timestamp: -1 });
db.audit_logs.createIndex(
{ expires_at: 1 },
{ expireAfterSeconds: 0 } // TTL index for retention
);
// Create default company
db.companies.insertOne({
_id: ObjectId(),
name: 'Default Company',
created_at: new Date(),
updated_at: new Date()
});
// Create admin user (password: admin123, bcrypt hash)
db.users.insertOne({
_id: ObjectId(),
email: 'admin@flow8.local',
password_hash: '$2a$12$nOUIs5kJ7naTuTFkWK1Be.g6Scot5aIyab.MQyqmqVH4.y3jC6sFm', // bcrypt of "admin123"
company_associations: [
{
company_id: db.companies.findOne()._id,
role: 'admin',
joined_at: new Date()
}
],
created_at: new Date(),
updated_at: new Date()
});

Network Configuration

Bridge Network

The backend network isolates flow8 services from the host:

networks:
backend:
driver: bridge

Benefits:

  • Services communicate by name (flow8 → mongo)
  • Isolated from host network
  • Easy to add more services (Redis, queue, etc.)

Port Exposure

services:
flow8:
ports:
- "4454:4454" # External:Internal
- "4445:4445" # MCP server (optional)
- "7701-7799:7701-7799" # Channel ports (optional)

Production Security:

Bind only to localhost:

ports:
- "127.0.0.1:4454:4454" # Only accessible from localhost
- "127.0.0.1:4445:4445"

Then use a reverse proxy for external access.

Volume Strategy

Data Persistence

volumes:
flow8-data:
driver: local
mongo-data:
driver: local

Named volumes are created automatically by Docker and persist between container restarts.

Backup Named Volumes

Terminal window
# Backup flow8-data volume
docker run --rm \
-v flow8-data:/data \
-v $(pwd):/backup \
ubuntu tar czf /backup/flow8-data-backup.tar.gz -C /data .
# Restore
docker run --rm \
-v flow8-data:/data \
-v $(pwd):/backup \
ubuntu tar xzf /backup/flow8-data-backup.tar.gz -C /data

Bind Mounts (Development)

volumes:
- .:/app/src # Mount source code for hot reload
- /app/src/vendor # But exclude vendor directory

Commands

Production

Terminal window
# Start services
docker-compose up -d
# View logs
docker-compose logs -f flow8
# Stop services
docker-compose down
# Restart MongoDB
docker-compose restart mongo
# Backup database
docker exec flow8-mongo mongodump --uri="mongodb://admin:password@localhost:27017" --out=/backup

Development

Terminal window
# Start with dev profile
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d
# Rebuild image
docker-compose build --no-cache
# Enter container shell
docker exec -it flow8-dev bash
# View real-time logs
docker-compose logs -f flow8

Local Debugging

Terminal window
# Start with Delve debugger
docker-compose -f docker-compose.local.yml up -d
# Connect Delve from IDE (port 2345)
# In VS Code: Add launch configuration for localhost:2345
# View logs
docker-compose logs -f flow8

Common Issues

”Cannot connect to MongoDB”

Error: connect: connection refused

Check MongoDB health:

Terminal window
docker-compose ps mongo
docker-compose logs mongo
docker exec flow8-mongo mongosh --eval "db.adminCommand('ping')"

“Volume permission denied”

Error: mkdir: cannot create directory '/app/data': Permission denied

Fix permissions:

Terminal window
docker-compose down
docker volume rm flow8-data
docker-compose up -d

“Port already in use”

Error: Bind for 0.0.0.0:4454 failed: port is already allocated

Find and stop conflicting service:

Terminal window
lsof -i :4454
kill -9 <PID>
# Or use different port
docker-compose -e "PORT=4455" up

MongoDB Credentials

Default credentials in compose files:

Username: admin
Password: ${MONGO_ROOT_PASSWORD} (from .env)

Change for production:

Terminal window
# Generate strong password
openssl rand -base64 32
# Update .env
MONGO_ROOT_PASSWORD=your_generated_password
# Recreate MongoDB (destroys data)
docker-compose down -v mongo
docker-compose up -d mongo

Scaling to Multiple Instances

For load balancing, deploy multiple flow8 instances:

version: '3.8'
services:
flow8-1:
image: flow8core:latest
ports:
- "4454:4454"
environment:
MONGODB_URI: mongodb://mongo:27017/flow8
CHANNEL_PORT_RANGE: 7701-7799 # Instance 1
flow8-2:
image: flow8core:latest
ports:
- "4455:4454"
environment:
MONGODB_URI: mongodb://mongo:27017/flow8
CHANNEL_PORT_RANGE: 7800-7899 # Instance 2 (separate range)
nginx:
image: nginx:latest
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- flow8-1
- flow8-2

nginx.conf:

upstream flow8_backend {
least_conn;
server flow8-1:4454;
server flow8-2:4454;
}
server {
listen 80;
server_name app.flow8.local;
location / {
proxy_pass http://flow8_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;
}
}