Skip to content

Docker Deployment

Docker Image Specifications

Base Image

The flow8 Docker image is built on a multi-stage Dockerfile with the following base:

FROM linuxserver/libreoffice:latest
# Includes:
# - LibreOffice (for document conversion)
# - ImageMagick (for image processing)
# - Tesseract OCR (for text extraction from images)

Why linuxserver/libreoffice?

  • Pre-configured with system dependencies (LibreOffice, ImageMagick, Tesseract)
  • Minimal attack surface
  • Regular security updates
  • English locale and fonts included

Installed System Packages

# Document & Image Processing
- liboffice (entire suite)
- imagemagick
- ghostscript (for PDF manipulation)
- tesseract-ocr (English models: eng)
# Network & System Tools
- curl
- wget
- ca-certificates (for TLS validation)
- openssl
# Compression & Archives
- zip
- unzip
- tar
- gzip
# Database Clients (for debugging)
- mongodb-org-tools (mongosh, mongodump)
- postgresql-client
- mysql-client

Binary Location

/app/flow8core

The compiled flow8 binary is placed here during multi-stage build.

Port Mappings

PortServiceProtocolPurposeRequired
4454flow8 APIHTTPMain application, REST API, WebSocket, UIYes
4445MCP ServerHTTPModel Context Protocol server (AI agents)No
7701-7799Channel PortsTCP/WebSocketAsync execution workersNo (unless channels enabled)

Example Docker Run

Terminal window
docker run \
-p 4454:4454 \
-p 4445:4445 \
-p 7701-7799:7701-7799 \
flow8core:latest

Port Binding Notes:

  • External Production: Bind main port via reverse proxy (nginx, Caddy, ALB)

    Terminal window
    # ✅ Production: reverse proxy handles 443 → 4454
    docker run -p 127.0.0.1:4454:4454 flow8core:latest
  • Development: Can bind directly

    Terminal window
    docker run -p 0.0.0.0:4454:4454 flow8core:latest
  • Channel Ports: Bind for multi-instance or long-running jobs

    Terminal window
    docker run -p 7701-7799:7701-7799 flow8core:latest

Volume Mounts

The application writes data to /app/data/ directory. Mount this volume for persistence:

Terminal window
docker run \
-v flow8-data:/app/data \
flow8core:latest

Directory Structure Inside Container

/app/
├── flow8core # Binary
├── data/ # Runtime data (mounted volume)
│ ├── db/ # JSON database snapshots
│ ├── data/ # User-uploaded files
│ │ ├── flows/
│ │ ├── attachments/
│ │ └── tmp/
│ ├── logs/ # Application logs
│ │ └── app.log
│ └── tmp/ # Temporary processing files
└── config/
└── config.yml # Runtime config

Volume Permissions

The container runs as unprivileged user (UID: 911):

Terminal window
# Ensure volume permissions
docker run \
-v flow8-data:/app/data \
-u 911:911 \
flow8core:latest

Environment Variables

Required Variables

Terminal window
# MongoDB Connection
MONGODB_URI=mongodb://mongo:27017/flow8
MONGODB_DB=flow8
# Server Configuration
SERVER_PORT=4454 # Default: 4454
ENCRYPTION_KEY=<generate: openssl rand -hex 128> # 256-char hex, REQUIRED for security
ENCRYPTION_KEY_SALT=<generate: openssl rand -hex 32> # 64-char hex, REQUIRED for security

Optional Variables

Terminal window
# MCP Server (for AI agent integration)
MCP_SERVER_ENABLED=true
MCP_SERVER_PORT=4445
# Channel Ports (for async execution)
CHANNEL_PORT_RANGE=7701-7799
CHANNEL_POOL_SIZE=100 # Max concurrent channel workers
# OAuth2 (Microsoft Office 365 SSO)
OAUTH2_CLIENT_ID=550e8400...
OAUTH2_CLIENT_SECRET=... # Encrypted
OAUTH2_REDIRECT_URI=https://app.flow8.io/auth/callback
# Component Defaults
DEFAULT_AI_COMPONENT=openai # or anthropic, mistral, ollama
DEFAULT_STORAGE_COMPONENT=s3 # or gcs, local
DEFAULT_DB_COMPONENT=postgres
# Retention & Cleanup
RETENTION_CLEANUP_INTERVAL=2m
RETENTION_AUDIT_CADENCE=30d
RETENTION_FLOWS_CADENCE=90d
# Logging
LOG_LEVEL=info # debug, info, warn, error
LOG_FILE=/app/data/logs/app.log
# Field Size Limits
AUDIT_LOG_FIELD_MAX_BYTES=4096
UNIT_OUTPUT_UI_MAX_BYTES=1048576
# Session & Auth
SESSION_TTL_HOURS=1
SESSION_COOKIE_SECURE=true
SESSION_COOKIE_HTTP_ONLY=true
SESSION_COOKIE_SAME_SITE=strict

Configuration File

Configuration is loaded from config/config.yml with environment variable substitution:

/app/config/config.yml
server:
port: ${SERVER_PORT} # Substituted from env
max_request_size_mb: 100
read_timeout_seconds: 30
write_timeout_seconds: 30
mongodb:
uri: ${MONGODB_URI}
database: ${MONGODB_DB}
max_pool_size: 100
ssl: true
encryption:
key_secret: ${ENCRYPTION_KEY}
key_salt: ${ENCRYPTION_KEY_SALT}
algorithm: argon2id
oauth2:
microsoft:
client_id: ${OAUTH2_CLIENT_ID}
client_secret: ${OAUTH2_CLIENT_SECRET}
redirect_uri: ${OAUTH2_REDIRECT_URI}
session:
ttl_hours: ${SESSION_TTL_HOURS}
cookie_secure: ${SESSION_COOKIE_SECURE}
cookie_http_only: ${SESSION_COOKIE_HTTP_ONLY}
cookie_same_site: ${SESSION_COOKIE_SAME_SITE}
# ... rest of config

Docker Run Example

Minimal Setup

Terminal window
docker run \
--name flow8 \
-e MONGODB_URI=mongodb://mongo:27017/flow8 \
-e SERVER_PORT=4454 \
-e ENCRYPTION_KEY=<output of: openssl rand -hex 128> \
-e ENCRYPTION_KEY_SALT=<output of: openssl rand -hex 32> \
-p 4454:4454 \
-v flow8-data:/app/data \
flow8core:latest

Production-Ready Setup

Terminal window
docker run \
--name flow8-prod \
--restart unless-stopped \
\
# Environment
-e MONGODB_URI=mongodb+srv://user:pass@cluster.mongodb.net/flow8 \
-e SERVER_PORT=4454 \
-e ENCRYPTION_KEY=$(cat /run/secrets/flow8_encryption_key) \
-e ENCRYPTION_KEY_SALT=$(cat /run/secrets/flow8_salt) \
-e OAUTH2_CLIENT_ID=$(cat /run/secrets/oauth2_client_id) \
-e OAUTH2_CLIENT_SECRET=$(cat /run/secrets/oauth2_client_secret) \
-e LOG_LEVEL=info \
-e SESSION_COOKIE_SECURE=true \
\
# Ports
-p 127.0.0.1:4454:4454 \
\
# Volumes
-v flow8-data:/app/data \
-v /etc/ssl/certs:/etc/ssl/certs:ro \
\
# Resource limits
--cpus=2 \
--memory=2g \
--memory-swap=4g \
\
# Health check
--health-cmd='curl -f http://localhost:4454/health || exit 1' \
--health-interval=30s \
--health-timeout=10s \
--health-retries=3 \
--health-start-period=40s \
\
flow8core:latest

Health Checks

Built-in Health Endpoint

Terminal window
# HTTP GET /health
curl http://localhost:4454/health
Response:
{
"status": "healthy",
"checks": {
"mongodb": "connected",
"config": "loaded",
"components": "initialized"
},
"timestamp": "2026-04-04T10:00:00Z"
}

Docker Health Check Configuration

HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD curl -f http://localhost:4454/health || exit 1

Docker Compose Health Check

services:
flow8:
image: flow8core:latest
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:4454/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
restart: unless-stopped

Resource Requirements

Minimum Requirements

CPU: 500m (0.5 cores)
Memory: 512 MB
Disk: 5 GB (data volume)

Suitable for:

  • Development
  • Testing
  • Small single-instance deployments
CPU: 2-4 cores
Memory: 2-4 GB
Disk: 50+ GB (data volume)

Suitable for:

  • Production single-instance
  • Multiple concurrent flows
  • Large document processing workloads

Large-Scale Deployment

CPU: 8+ cores per instance
Memory: 8+ GB per instance
Disk: 100+ GB per instance (shared storage for S3/GCS)

Suitable for:

  • Multi-instance cluster
  • 1000+ concurrent plays
  • Enterprise SLA requirements

Docker Compose

See the Docker Compose deployment guide for complete examples with MongoDB and other services.

Image Scanning & Security

Scan for Vulnerabilities

Terminal window
# Using trivy
trivy image flow8core:latest
# Using Snyk
snyk container test flow8core:latest
# Using AWS ECR
aws ecr start-image-scan --repository-name flow8core --image-id imageTag=latest

Image Signing

Sign images with Cosign:

Terminal window
cosign sign --key cosign.key ghcr.io/osbits/flow8core:latest

Verify on pull:

Terminal window
cosign verify --key cosign.pub ghcr.io/osbits/flow8core:latest

Logging

Application Logs

Logs are written to /app/data/logs/app.log:

Terminal window
# View logs
docker logs flow8-prod
# Stream logs
docker logs -f flow8-prod
# Tail last 100 lines
docker logs --tail 100 flow8-prod

Log Format

2026-04-04T10:23:45.123Z INFO http GET /api/v1/flows?page=1 200 45ms
2026-04-04T10:23:46.456Z ERROR auth login failed: wrong_password user_id=user_123

Log Level Configuration

Terminal window
docker run \
-e LOG_LEVEL=debug \
flow8core:latest

Valid levels: debug, info, warn, error

Stopping & Cleanup

Terminal window
# Stop container gracefully (30s timeout)
docker stop flow8-prod
# Stop forcefully
docker kill flow8-prod
# Remove container
docker rm flow8-prod
# Remove volume (WARNING: data loss)
docker volume rm flow8-data

Common Issues

”Connection refused” to MongoDB

Error: connect: connection refused

Solution: Ensure MongoDB is accessible

Terminal window
# Test connectivity
docker exec flow8-prod curl -s mongodb://mongo:27017 || echo "MongoDB not accessible"
# Check MongoDB container
docker logs mongo

”Permission denied” on volumes

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

Solution: Set volume permissions

Terminal window
# Create volume with correct permissions
docker volume create flow8-data
# Or use explicit path with correct permissions
mkdir -p /data/flow8
chmod 755 /data/flow8
docker run -v /data/flow8:/app/data ...

Out of memory

OOMKilled

Solution: Increase memory limit or adjust GC

Terminal window
docker run \
--memory=4g \
--memory-swap=8g \
-e GOMAXPROCS=4 \
flow8core:latest

Docker Registry

Push to Registry

Terminal window
docker tag flow8core:latest myregistry.azurecr.io/flow8core:latest
docker push myregistry.azurecr.io/flow8core:latest

Private Registry Authentication

Terminal window
# Log in
docker login myregistry.azurecr.io
# Store credentials in Kubernetes Secret
kubectl create secret docker-registry flow8-registry \
--docker-server=myregistry.azurecr.io \
--docker-username=user \
--docker-password=password
# Use in Kubernetes Pod
spec:
imagePullSecrets:
- name: flow8-registry
containers:
- image: myregistry.azurecr.io/flow8core:latest