Docker Deployment
This guide covers deploying License Monitor and License Server Detail using Docker containers.
Overview
Section titled “Overview”Docker deployment provides:
- Consistent environments across development and production
- Easy scaling and orchestration
- Simplified dependency management
- Portable deployments
License Monitor Docker Image
Section titled “License Monitor Docker Image”Building the Image
Section titled “Building the Image”# Dockerfile for License MonitorFROM debian:bookworm-slim as runtime
# Install runtime dependenciesRUN apt-get update && apt-get install -y \ ca-certificates \ python3 \ python3-pip \ && rm -rf /var/lib/apt/lists/*
# Create non-root userRUN useradd -r -s /bin/false -u 1000 license-monitor
# Copy binary from releaseCOPY --chown=license-monitor:license-monitor license_monitor /usr/local/bin/RUN chmod +x /usr/local/bin/license_monitor
# Copy configurationCOPY --chown=license-monitor:license-monitor config.toml /etc/license-monitor/COPY --chown=license-monitor:license-monitor servers/ /etc/license-monitor/servers/
# Create directoriesRUN mkdir -p /var/log/license-monitor \ && chown license-monitor:license-monitor /var/log/license-monitor
# Switch to non-root userUSER license-monitor
# Expose API portEXPOSE 8080
# Health checkHEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \ CMD curl -f http://localhost:8080/api/health || exit 1
# Run License MonitorENTRYPOINT ["/usr/local/bin/license_monitor"]CMD ["--config", "/etc/license-monitor/config.toml", "--daemon"]Building
Section titled “Building”# Download the latest release binarycurl -L -o license_monitor \ "https://github.com/keithce/license_monitor/releases/latest/download/license_monitor-linux-x86_64"
# Build the Docker imagedocker build -t license-monitor:latest .License Server Detail Docker Image
Section titled “License Server Detail Docker Image”Building the Image
Section titled “Building the Image”# Dockerfile for License Server DetailFROM oven/bun:1.3 AS base
# Install dependenciesFROM base AS depsWORKDIR /appCOPY package.json bun.lockb ./RUN bun install --frozen-lockfile
# Build applicationFROM base AS builderWORKDIR /appCOPY --from=deps /app/node_modules ./node_modulesCOPY . .
# Build environment variables (non-secret)ARG NEXT_PUBLIC_API_BASE_URLARG NEXT_PUBLIC_API_TIMEOUT_MS=15000ARG NEXT_PUBLIC_LOG_LEVEL=info
ENV NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URLENV NEXT_PUBLIC_API_TIMEOUT_MS=$NEXT_PUBLIC_API_TIMEOUT_MSENV NEXT_PUBLIC_LOG_LEVEL=$NEXT_PUBLIC_LOG_LEVELENV NEXT_TELEMETRY_DISABLED=1
RUN bun run build
# Production imageFROM base AS runnerWORKDIR /app
ENV NODE_ENV=productionENV NEXT_TELEMETRY_DISABLED=1
# Create non-root userRUN addgroup --system --gid 1001 nodejsRUN adduser --system --uid 1001 nextjs
# Copy built assetsCOPY --from=builder /app/public ./publicCOPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000ENV HOSTNAME="0.0.0.0"
# Health checkHEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ CMD curl -f http://localhost:3000/api/health || exit 1
CMD ["bun", "server.js"]Building
Section titled “Building”docker build \ --build-arg NEXT_PUBLIC_API_BASE_URL=https://api.example.com \ -t license-server-detail:latest .Docker Compose
Section titled “Docker Compose”Complete Stack
Section titled “Complete Stack”version: '3.8'
services: license-monitor: build: context: ./license-monitor dockerfile: Dockerfile image: license-monitor:latest container_name: license-monitor restart: unless-stopped ports: - "8080:8080" volumes: - ./config/license-monitor:/etc/license-monitor:ro - license-monitor-logs:/var/log/license-monitor environment: - LICENSE_MONITOR_LOG_LEVEL=warn - LICENSE_MONITOR_API_ENABLED=true - LICENSE_MONITOR_BIND_ADDRESS=0.0.0.0 - ALLOW_PUBLIC_BIND=true networks: - license-network healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/api/health"] interval: 30s timeout: 5s retries: 3 start_period: 10s
license-server-detail: build: context: ./license-server-detail dockerfile: Dockerfile args: - NEXT_PUBLIC_API_BASE_URL=http://license-monitor:8080 image: license-server-detail:latest container_name: license-server-detail restart: unless-stopped ports: - "3000:3000" environment: - LICENSE_MONITOR_BASE_URL=http://license-monitor:8080 - LICENSE_MONITOR_API_KEY=${LICENSE_MONITOR_API_KEY} - AUTH_OKTA_ID=${AUTH_OKTA_ID} - AUTH_OKTA_SECRET=${AUTH_OKTA_SECRET} - AUTH_OKTA_ISSUER=${AUTH_OKTA_ISSUER} - AUTH_SECRET=${AUTH_SECRET} - NEXTAUTH_URL=${NEXTAUTH_URL} depends_on: license-monitor: condition: service_healthy networks: - license-network healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"] interval: 30s timeout: 5s retries: 3 start_period: 30s
nginx: image: nginx:alpine container_name: nginx-proxy restart: unless-stopped ports: - "80:80" - "443:443" volumes: - ./config/nginx/nginx.conf:/etc/nginx/nginx.conf:ro - ./config/nginx/ssl:/etc/nginx/ssl:ro depends_on: - license-server-detail networks: - license-network
volumes: license-monitor-logs:
networks: license-network: driver: bridgeEnvironment File
Section titled “Environment File”# License MonitorLICENSE_MONITOR_API_KEY=your-secure-api-key
# Okta AuthenticationAUTH_OKTA_ID=your-okta-client-idAUTH_OKTA_SECRET=your-okta-client-secretAUTH_OKTA_ISSUER=https://your-org.okta.com/oauth2/default
# NextAuthAUTH_SECRET=your-32-character-secretNEXTAUTH_URL=https://dashboard.example.comRunning with Docker Compose
Section titled “Running with Docker Compose”Start Services
Section titled “Start Services”# Start all servicesdocker-compose up -d
# View logsdocker-compose logs -f
# View specific service logsdocker-compose logs -f license-monitorStop Services
Section titled “Stop Services”# Stop all servicesdocker-compose down
# Stop and remove volumesdocker-compose down -vUpdate Services
Section titled “Update Services”# Pull latest imagesdocker-compose pull
# Rebuild and restartdocker-compose up -d --build
# Zero-downtime update (with multiple replicas)docker-compose up -d --scale license-monitor=2# Wait for new container to be healthydocker-compose up -d --scale license-monitor=1Volume Configuration
Section titled “Volume Configuration”License Monitor Volumes
Section titled “License Monitor Volumes”| Path | Purpose | Recommended |
|---|---|---|
/etc/license-monitor/ | Configuration | Read-only bind mount |
/var/log/license-monitor/ | Log files | Named volume |
/etc/license-monitor/servers/ | Parser scripts | Read-only bind mount |
License Server Detail Volumes
Section titled “License Server Detail Volumes”| Path | Purpose | Recommended |
|---|---|---|
/app/.next/cache/ | Build cache | Named volume |
/app/public/ | Static assets | Read-only bind mount |
Health Checks
Section titled “Health Checks”License Monitor
Section titled “License Monitor”healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8080/api/health"] interval: 30s timeout: 5s retries: 3 start_period: 10sLicense Server Detail
Section titled “License Server Detail”healthcheck: test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"] interval: 30s timeout: 5s retries: 3 start_period: 30sNetworking
Section titled “Networking”Internal Communication
Section titled “Internal Communication”Services communicate over the license-network bridge network using container names as hostnames:
license-server-detail → license-monitor:8080nginx → license-server-detail:3000External Access
Section titled “External Access”Only expose necessary ports to the host:
ports: - "443:443" # HTTPS via nginx # Don't expose internal ports directlyScaling
Section titled “Scaling”Horizontal Scaling
Section titled “Horizontal Scaling”# Scale License Monitor instancesdocker-compose up -d --scale license-monitor=3
# Use load balancer for multiple instancesResource Limits
Section titled “Resource Limits”services: license-monitor: deploy: resources: limits: cpus: '1.0' memory: 512M reservations: cpus: '0.25' memory: 256MLogging
Section titled “Logging”JSON Logging
Section titled “JSON Logging”Configure JSON logging for container environments:
[daemon]log_format = "json"log_level = "info"Log Aggregation
Section titled “Log Aggregation”services: license-monitor: logging: driver: "json-file" options: max-size: "10m" max-file: "3" labels: "application,environment" labels: application: "license-monitor" environment: "production"Security Considerations
Section titled “Security Considerations”Run as Non-Root
Section titled “Run as Non-Root”USER license-monitor # or nextjsRead-Only Filesystem
Section titled “Read-Only Filesystem”services: license-monitor: read_only: true tmpfs: - /tmp volumes: - license-monitor-logs:/var/log/license-monitorSecret Management
Section titled “Secret Management”services: license-server-detail: secrets: - auth_secret - okta_secret
secrets: auth_secret: external: true okta_secret: external: trueservices: license-server-detail: env_file: - .env.productionNext Steps
Section titled “Next Steps”- Systemd Services - Native Linux service deployment
- Reverse Proxy - Nginx/Apache configuration
- Production Setup - Production hardening