Overview
Duckling provides production-ready Docker images with multi-stage builds for optimal size and security. Both development and production environments use Docker for consistency.
Quick Start
Development Environment
The development environment uses docker-compose with hot reload enabled:
# Start all services
docker-compose up -d
# View server logs
docker-compose logs -f duckdb-server
# View frontend logs
docker-compose logs -f duckdb-frontend
# Stop all services
docker-compose down
Service URLs:
Production Deployment
# Build production image
docker build -f Dockerfile -t duckling:latest .
# Run production container
docker run -d \
--name duckling \
-p 3000:3000 \
-p 3307:3307 \
-v ./data:/app/data \
-v ./logs:/app/logs \
--env-file .env \
duckling:latest
Docker Compose Configuration
The docker-compose.yml defines two services for development:
Server Service
services :
duckdb-server :
container_name : duckling-server
build :
context : .
dockerfile : docker/server.Dockerfile
ports :
- "3001:3000" # HTTP API
- "3307:3307" # MySQL protocol
env_file :
- .env
volumes :
- ./data:/app/data # DuckDB database files
- ./logs:/app/logs # Application logs
# Hot reload source mounts
- ./packages/server/src:/app/packages/server/src
- ./packages/shared/src:/app/packages/shared/src
restart : unless-stopped
networks :
- duckdb-network
Key Configuration:
Port 3001 : HTTP API server
Port 3307 : MySQL wire protocol (connect with any MySQL client)
Volume /app/data : Persistent storage for DuckDB files and databases.json
Volume /app/logs : Application logs
Hot Reload : Source code mounted for development
Frontend Service
duckdb-frontend :
container_name : duckling-frontend
build :
context : .
dockerfile : docker/frontend.Dockerfile
ports :
- "3000:3000"
environment :
- NODE_ENV=${NODE_ENV:-development}
- NUXT_PUBLIC_API_BASE=http://localhost:3001
volumes :
# Hot reload - mount source code
- ./packages/frontend/app:/app/packages/frontend/app
- ./packages/frontend/nuxt.config.ts:/app/packages/frontend/nuxt.config.ts
restart : unless-stopped
depends_on :
- duckdb-server
networks :
- duckdb-network
Network Configuration
networks :
duckdb-network :
name : duckling-network
driver : bridge
Both services communicate over a dedicated bridge network for isolation.
Production Dockerfile
The production Dockerfile uses a multi-stage build for efficiency:
Stage 1: Builder
FROM node:20-slim AS builder
WORKDIR /app
# Install build dependencies
RUN apt-get update && apt-get install -y \
python3 \
make \
g++ \
libsqlite3-dev \
&& rm -rf /var/lib/apt/lists/*
# Install pnpm
RUN npm install -g [email protected]
# Copy workspace config
COPY pnpm-workspace.yaml package.json pnpm-lock.yaml .npmrc ./
COPY packages/shared/package.json ./packages/shared/
COPY packages/server/package.json ./packages/server/
COPY packages/frontend/package.json ./packages/frontend/
# Install dependencies
RUN pnpm install --frozen-lockfile --shamefully-hoist && pnpm rebuild
# Copy source and build
COPY packages/shared ./packages/shared
COPY packages/server ./packages/server
COPY packages/frontend ./packages/frontend
RUN pnpm build:shared && \
pnpm build:server && \
pnpm build:frontend
Stage 2: Production Runtime
FROM node:20-slim AS production
WORKDIR /app
# Install runtime dependencies only
RUN apt-get update && apt-get install -y \
libsqlite3-dev \
&& rm -rf /var/lib/apt/lists/*
RUN npm install -g [email protected]
# Copy workspace config
COPY pnpm-workspace.yaml package.json pnpm-lock.yaml .npmrc ./
COPY packages/shared/package.json ./packages/shared/
COPY packages/server/package.json ./packages/server/
# Install production dependencies only
RUN pnpm install --prod --frozen-lockfile --shamefully-hoist
# Copy built artifacts
COPY --from=builder /app/packages/shared/dist ./packages/shared/dist
COPY --from=builder /app/packages/server/dist ./packages/server/dist
# Copy frontend build to server's public directory
RUN rm -rf ./packages/server/public
COPY --from=builder /app/packages/frontend/.output/public ./packages/server/public
# Create data directories
RUN mkdir -p /var/lib/duckdb /app/data /app/logs
# Set production environment
ENV NODE_ENV=production
ENV NODE_OPTIONS= "--max-old-space-size=8192 --expose-gc"
EXPOSE 3000
CMD [ "pnpm" , "start:server" ]
Production Optimizations:
Multi-stage build : Separates build dependencies from runtime
Single executable : Server serves both API and frontend
8GB heap : --max-old-space-size=8192 for large sync operations
Manual GC : --expose-gc for memory cleanup
Production deps only : No devDependencies in final image
Volume Mounts
Required Volumes
DuckDB database files and configuration
databases.json - Multi-database configuration
{database_id}.db - DuckDB database files
backups/ - Local backup directory
metadata/ - Watermark and sync metadata
Host Mount : ./data:/app/data
Application logs Winston-based structured logs for debugging and monitoring. Host Mount : ./logs:/app/logs
Development Volumes (Hot Reload)
In development mode, source code is mounted for hot reload:
volumes :
# Server hot reload
- ./packages/server/src:/app/packages/server/src
- ./packages/shared/src:/app/packages/shared/src
# Frontend hot reload
- ./packages/frontend/app:/app/packages/frontend/app
- ./packages/frontend/nuxt.config.ts:/app/packages/frontend/nuxt.config.ts
Hot Reload : Changes to .ts and .vue files automatically apply without container restart.
Server uses nodemon (auto-restart)
Frontend uses Nuxt HMR (hot module replacement)
Port Mapping
HTTP Server Port Main API and frontend serving port.
Development: 3001:3000 (server), 3000:3000 (frontend)
Production: 3000:3000 (single container)
MySQL Protocol Port MySQL wire protocol compatibility. Connect using any MySQL client: mysql -h localhost -P 3307 -u duckling -p
Defaults to using DUCKLING_API_KEY as password.
Networking
Bridge Network
Services communicate over a dedicated bridge network:
networks :
duckdb-network :
name : duckling-network
driver : bridge
Inter-service communication:
Frontend → Server: http://duckdb-server:3000
External → Server: http://localhost:3001
External → Frontend: http://localhost:3000
Docker Commands
Development
# Start services
docker-compose up -d
# Rebuild after package.json changes
docker-compose up -d --build
# View logs
docker-compose logs -f duckdb-server
docker-compose logs -f duckdb-frontend
# Execute commands inside container
docker exec duckling-server pnpm run build:server
docker exec duckling-server node packages/server/dist/cli.js health
# Stop services
docker-compose down
# Remove volumes (data loss!)
docker-compose down -v
Production
# Build image
docker build -t duckling:latest .
# Run container
docker run -d \
--name duckling \
-p 3000:3000 \
-p 3307:3307 \
-v $( pwd ) /data:/app/data \
-v $( pwd ) /logs:/app/logs \
--env-file .env \
--restart unless-stopped \
duckling:latest
# View logs
docker logs -f duckling
# Execute CLI commands
docker exec duckling node packages/server/dist/cli.js status
# Stop container
docker stop duckling
# Remove container
docker rm duckling
When to Rebuild
DO rebuild (--build flag) when:
Adding/removing npm packages (package.json changes)
Changing Dockerfile or docker-compose.yml
Updating system dependencies (apt packages)
Major structural changes (monorepo reorganization)
DON’T rebuild for:
Code changes in .ts or .vue files (hot reload)
Configuration changes in .env files
View/template changes in public/ directory
Component changes in frontend app/ directory
Health Checks
Add Docker health checks for production:
services :
duckdb-server :
healthcheck :
test : [ "CMD" , "curl" , "-f" , "http://localhost:3000/health" ]
interval : 30s
timeout : 10s
retries : 3
start_period : 40s
Resource Limits
Recommended resource limits for production:
services :
duckdb-server :
deploy :
resources :
limits :
cpus : '4'
memory : 16G
reservations :
cpus : '2'
memory : 8G
Memory Guidelines:
Minimum : 4GB for small databases (<10GB)
Recommended : 8-16GB for production workloads
Large datasets : 16-32GB for 100GB+ databases
Troubleshooting
Container won’t start
# Check logs
docker-compose logs duckdb-server
# Check environment variables
docker-compose config
# Verify volumes exist
ls -la ./data ./logs
Hot reload not working
# Restart container
docker-compose restart duckdb-server
# Check volume mounts
docker inspect duckling-server | grep Mounts -A 20
Permission issues
# Fix data directory permissions
sudo chown -R $USER : $USER ./data ./logs
# Or run with correct user
docker-compose run --user $( id -u ) : $( id -g ) duckdb-server
Out of memory
# Increase Node.js heap size
ENV NODE_OPTIONS="--max-old-space-size=16384"
# Increase Docker memory limit
docker update --memory 16g duckling
Next Steps
Configuration Configure environment variables and settings
Production Guide Production deployment best practices
Security Secure your Duckling deployment