This guide provides deep technical details about LiveCodes Docker deployment. For a quick start, see the Self-Hosting Guide.
Dockerfile Architecture
LiveCodes uses a multi-stage Docker build for optimal image size and security:
Stage 1: Builder
FROM node:24.4.1-alpine3.22 AS builder
RUN apk update --no-cache && apk add --no-cache git
WORKDIR /app
# Copy package files for all workspaces
COPY package*.json ./
COPY docs/package*.json docs/
COPY storybook/package*.json storybook/
COPY server/package*.json server/
RUN npm ci
COPY . .
Key features:
- Uses Alpine Linux for minimal image size
- Installs dependencies before copying source for better layer caching
- Supports monorepo structure with multiple package.json files
Build Arguments
The builder stage accepts these build arguments:
ARG SELF_HOSTED
ARG SELF_HOSTED_SHARE
ARG SELF_HOSTED_BROADCAST
ARG BROADCAST_PORT
ARG SANDBOX_HOST_NAME
ARG SANDBOX_PORT
ARG FIREBASE_CONFIG
ARG DOCS_BASE_URL
ARG NODE_OPTIONS
These are compile-time constants embedded into the application bundle.
Conditional Build
RUN if [ "$DOCS_BASE_URL" == "null" ]; \
then npm run build:app; \
else npm run build; \
fi
build:app: Builds only the application (no documentation)
build: Builds application + documentation site
Stage 2: Production Server
FROM node:24.4.1-alpine3.22 AS server
# Create non-root user
RUN addgroup -S appgroup
RUN adduser -S appuser -G appgroup
RUN mkdir -p /srv && chown -R appuser:appgroup /srv
USER appuser
WORKDIR /srv
COPY server/package*.json ./
RUN npm ci
# Copy build artifacts from builder stage
COPY --from=builder /app/build/ build/
COPY functions/ functions/
COPY server/src/ server/src/
COPY src/livecodes/html/sandbox/ server/src/sandbox/
CMD ["node", "server/src/app.ts"]
Security features:
- Runs as non-root user (
appuser)
- Minimal production dependencies only
- Single responsibility: serve the application
Docker Compose Stack
The complete stack consists of three services:
App Service
app:
build:
context: .
dockerfile: Dockerfile
args:
- SELF_HOSTED=true
- SELF_HOSTED_SHARE=${SELF_HOSTED_SHARE:-true}
- SELF_HOSTED_BROADCAST=${SELF_HOSTED_BROADCAST:-true}
- BROADCAST_PORT=${BROADCAST_PORT:-3030}
- SANDBOX_HOST_NAME=${SANDBOX_HOST_NAME:-${HOST_NAME:-livecodes.localhost}}
- SANDBOX_PORT=${SANDBOX_PORT:-8090}
- FIREBASE_CONFIG=${FIREBASE_CONFIG:-}
- DOCS_BASE_URL=${DOCS_BASE_URL:-null}
- LOCAL_MODULES=${LOCAL_MODULES:-false}
- NODE_OPTIONS=--max-old-space-size=4096
restart: unless-stopped
environment:
- SELF_HOSTED=true
- HOST_NAME=${HOST_NAME:-livecodes.localhost}
- PORT=${PORT:-443}
- BROADCAST_TOKENS=${BROADCAST_TOKENS:-}
- SANDBOX_HOST_NAME=${SANDBOX_HOST_NAME:-${HOST_NAME:-livecodes.localhost}}
- SANDBOX_PORT=${SANDBOX_PORT:-8090}
- LOG_URL=${LOG_URL:-null}
- VALKEY_HOST=valkey
- VALKEY_PORT=6379
- NODE_OPTIONS=--max-old-space-size=4096
volumes:
- ./assets:/srv/build/assets
depends_on:
- valkey
Key features:
- Automatic restart on failure
- Custom assets via volume mount
- Connects to Valkey for data persistence
- 4GB memory allocation for Node.js
Valkey Service (Redis Alternative)
valkey:
image: valkey/valkey:8.1.2-alpine3.22
restart: on-failure
volumes:
- valkey-data:/data
command:
[
"sh",
"-c",
'if [ "$SELF_HOSTED_SHARE" != "false" ]; then valkey-server --save 60 1 --loglevel warning; fi',
]
Features:
- Persists data to named volume
valkey-data
- Auto-saves every 60 seconds if ≥1 change
- Only runs if sharing is enabled
- Minimal logging (warnings only)
Valkey is a Redis-compatible database. You can substitute with Redis if preferred.
Server Service (Caddy)
server:
image: caddy:2.10.0-alpine
entrypoint: ["/bin/sh", "./entrypoint.sh"]
command:
[
"caddy",
"run",
"--config",
"/etc/caddy/Caddyfile",
"--adapter",
"caddyfile",
]
restart: unless-stopped
ports:
- "80:80"
- "${PORT:-443}:${PORT:-443}"
- "${SANDBOX_PORT:-8090}:${SANDBOX_PORT:-8090}"
- "${BROADCAST_PORT:-3030}:${BROADCAST_PORT:-3030}"
environment:
- HOST_NAME=${HOST_NAME:-livecodes.localhost}
- PORT=${PORT:-443}
- SANDBOX_HOST_NAME=${SANDBOX_HOST_NAME:-${HOST_NAME:-livecodes.localhost}}
- SANDBOX_PORT=${SANDBOX_PORT:-8090}
- BROADCAST_PORT=${BROADCAST_PORT:-3030}
volumes:
- ./server/caddy/entrypoint.sh:/srv/entrypoint.sh
- ./server/caddy/Caddyfile:/etc/caddy/Caddyfile
- ./server/data/caddy/data:/data
- ./server/data/caddy/config:/config
depends_on:
- app
Features:
- Automatic HTTPS with Let’s Encrypt
- Reverse proxy for all services
- HTTP to HTTPS redirect
- Persistent TLS certificates
Volume Configuration
Named Volumes
volumes:
valkey-data:
name: livecodes-share-data
The livecodes-share-data volume persists shared project data across container restarts and updates.
Bind Mounts
Custom assets:
volumes:
- ./assets:/srv/build/assets
Place custom files in ./assets/ to override defaults.
Caddy configuration:
volumes:
- ./server/caddy/Caddyfile:/etc/caddy/Caddyfile
- ./server/data/caddy/data:/data
- ./server/data/caddy/config:/config
Persists TLS certificates and Caddy configuration.
Network Architecture
Default Bridge Network
All services communicate via Docker’s default bridge network:
┌─────────────────────────────────────────┐
│ Docker Bridge Network │
│ │
│ ┌─────────┐ ┌─────────┐ │
│ │ Caddy │───▶│ App │ │
│ │ (proxy) │ │ (node) │ │
│ └─────────┘ └─────────┘ │
│ │ │ │
│ │ ┌────▼─────┐ │
│ │ │ Valkey │ │
│ │ │ (redis) │ │
│ │ └──────────┘ │
│ │ │
└───────┼───────────────────────────────┘
│
▼
External Access
(ports 80, 443, 8090, 3030)
Service Resolution
Services resolve each other by name:
- App → Valkey:
valkey:6379
- Caddy → App:
app:443
Custom Build Examples
Build Without Documentation
docker build \
--build-arg SELF_HOSTED=true \
--build-arg DOCS_BASE_URL=null \
--build-arg SANDBOX_HOST_NAME=sandbox.example.com \
-t livecodes:latest .
Build with Custom Firebase
docker build \
--build-arg FIREBASE_CONFIG='{"apiKey":"...","authDomain":"..."}' \
-t livecodes:custom .
Production Build with Optimizations
docker build \
--build-arg NODE_OPTIONS="--max-old-space-size=8192" \
--build-arg SELF_HOSTED=true \
--no-cache \
-t livecodes:production .
Docker Compose Profiles
Development Profile
# docker-compose.dev.yml
services:
app:
build:
args:
- DOCS_BASE_URL=http://localhost:3000
environment:
- NODE_ENV=development
ports:
- "3000:3000"
Run with: docker-compose -f docker-compose.yml -f docker-compose.dev.yml up
Production Profile
# docker-compose.prod.yml
services:
app:
environment:
- NODE_ENV=production
deploy:
resources:
limits:
cpus: '2'
memory: 4G
reservations:
cpus: '1'
memory: 2G
Health Checks
Add health checks to ensure service availability:
app:
healthcheck:
test: ["CMD", "node", "-e", "require('http').get('http://localhost:443/', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
valkey:
healthcheck:
test: ["CMD", "valkey-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
Scaling Considerations
Horizontal Scaling
When scaling horizontally, use external Redis/Valkey with shared access, not the container instance.
Load Balancing
server:
environment:
- CADDY_UPSTREAM="app:443 app-2:443 app-3:443"
Security Hardening
Read-Only Root Filesystem
app:
read_only: true
tmpfs:
- /tmp
- /srv/.npm
Drop Capabilities
app:
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
Security Options
app:
security_opt:
- no-new-privileges:true
pids_limit: 100
Backup and Recovery
Backup Valkey Data
# Create backup
docker run --rm \
-v livecodes-share-data:/data \
-v $(pwd)/backups:/backup \
alpine tar czf /backup/valkey-backup-$(date +%Y%m%d).tar.gz /data
Restore from Backup
# Restore backup
docker run --rm \
-v livecodes-share-data:/data \
-v $(pwd)/backups:/backup \
alpine tar xzf /backup/valkey-backup-20260303.tar.gz -C /
Monitoring
Container Stats
docker stats --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}"
Logs Aggregation
app:
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"
labels: "service,environment"
External Monitoring
Integrate with Prometheus:
app:
labels:
- "prometheus.io/scrape=true"
- "prometheus.io/port=3000"
Troubleshooting
Build fails with out of memory
Increase Docker memory limit:docker build --memory=8g -t livecodes .
Check volume permissions:sudo chown -R 1000:1000 ./assets
sudo chown -R 1000:1000 ./server/data
Services can't communicate
Verify network:docker network inspect livecodes_default
docker-compose exec app ping valkey
Next Steps
Services Architecture
Understand the internal services
Security Model
Security best practices
Performance
Optimize Docker performance
Self-Hosting
Back to self-hosting guide