Docker Deployment
Deploying GoPie with Docker and Docker Compose
This guide covers deploying GoPie using Docker and Docker Compose, including development, staging, and production configurations.
Prerequisites
- Docker Engine 20.10+
- Docker Compose 2.0+
- 8GB RAM minimum (16GB recommended)
- 20GB free disk space
Quick Start
Development Deployment
# Clone the repository
git clone https://github.com/your-org/gopie.git
cd gopie
# Copy environment files
cp .env.example .env
# Start all services
docker-compose up -d
# Check service health
docker-compose ps
docker-compose logs -f
# Access the application
# Web UI: http://localhost:3000
# API: http://localhost:8000
# Chat Server: http://localhost:8001Development Without Auth
# Use no-auth configuration for faster development
docker-compose -f docker-compose-noauth.yaml up -dDocker Images
Web Frontend Dockerfile
# web/Dockerfile
FROM node:20-alpine AS base
# Install dependencies only when needed
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
# Install dependencies
COPY package.json package-lock.json* ./
RUN npm ci
# Rebuild the source code only when needed
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
# Build the application
ENV NEXT_TELEMETRY_DISABLED 1
RUN npm run build
# Production image, copy all the files and run next
FROM base AS runner
WORKDIR /app
ENV NODE_ENV production
ENV NEXT_TELEMETRY_DISABLED 1
# Create non-root user
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
# Copy built application
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
# Copy WASM files
COPY --from=builder /app/public/wasm ./public/wasm
USER nextjs
EXPOSE 3000
ENV PORT 3000
ENV HOSTNAME "0.0.0.0"
CMD ["node", "server.js"]Go Backend Dockerfile
# server/Dockerfile
# Build stage
FROM golang:1.21-alpine AS builder
RUN apk add --no-cache git gcc musl-dev sqlite-dev
WORKDIR /app
# Copy go mod files
COPY go.mod go.sum ./
RUN go mod download
# Copy source code
COPY . .
# Build the application
RUN CGO_ENABLED=1 GOOS=linux go build -a -installsuffix cgo -o gopie main.go
# Final stage
FROM alpine:latest
RUN apk --no-cache add ca-certificates tzdata
WORKDIR /root/
# Copy the binary
COPY --from=builder /app/gopie .
# Copy migration files
COPY --from=builder /app/infrastructure/postgres/migrations ./migrations
# Install goose for migrations
RUN apk add --no-cache curl
RUN curl -fsSL \
https://raw.githubusercontent.com/pressly/goose/master/install.sh \
| sh
EXPOSE 8000
CMD ["./gopie", "serve"]Python Chat Server Dockerfile
# chat-server/Dockerfile
FROM python:3.11-slim
# Install system dependencies
RUN apt-get update && apt-get install -y \
gcc \
g++ \
curl \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Install Python dependencies
COPY pyproject.toml .
RUN pip install --no-cache-dir uv && \
uv pip install --system -e .
# Copy application code
COPY . .
# Create non-root user
RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
USER appuser
EXPOSE 8001
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8001"]Docker Compose Configuration
Full Stack Development
# docker-compose.yml
version: '3.8'
services:
# PostgreSQL Database
postgres:
image: postgres:16-alpine
environment:
POSTGRES_USER: gopie
POSTGRES_PASSWORD: gopie
POSTGRES_DB: gopie
volumes:
- postgres_data:/var/lib/postgresql/data
- ./scripts/init-db.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432"
healthcheck:
test: ["CMD-SHELL", "pg_isready -U gopie"]
interval: 10s
timeout: 5s
retries: 5
# MinIO S3-compatible storage
minio:
image: minio/minio:latest
command: server /data --console-address ":9001"
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin
MINIO_DEFAULT_BUCKETS: gopie-datasets:public,gopie-exports:private
volumes:
- minio_data:/data
ports:
- "9000:9000"
- "9001:9001"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 20s
retries: 3
# Qdrant Vector Database
qdrant:
image: qdrant/qdrant:latest
volumes:
- qdrant_data:/qdrant/storage
ports:
- "6333:6333"
- "6334:6334"
environment:
QDRANT__LOG_LEVEL: INFO
# Zitadel Authentication
zitadel:
image: ghcr.io/zitadel/zitadel:latest
command: 'start-from-init --masterkey "MasterkeyNeedsToHave32Characters" --tlsMode disabled'
environment:
ZITADEL_DATABASE_POSTGRES_HOST: postgres
ZITADEL_DATABASE_POSTGRES_PORT: 5432
ZITADEL_DATABASE_POSTGRES_DATABASE: zitadel
ZITADEL_DATABASE_POSTGRES_USER_USERNAME: zitadel
ZITADEL_DATABASE_POSTGRES_USER_PASSWORD: zitadel
ZITADEL_DATABASE_POSTGRES_USER_SSL_MODE: disable
ZITADEL_EXTERNALSECURE: false
ZITADEL_EXTERNALPORT: 4455
ZITADEL_EXTERNALHOST: localhost
depends_on:
postgres:
condition: service_healthy
ports:
- "4455:8080"
# Go Backend Server
gopie-server:
build:
context: ./server
dockerfile: Dockerfile
environment:
DATABASE_URL: postgres://gopie:gopie@postgres:5432/gopie?sslmode=disable
S3_ENDPOINT: http://minio:9000
S3_ACCESS_KEY: minioadmin
S3_SECRET_KEY: minioadmin
S3_BUCKET_NAME: gopie-datasets
S3_REGION: us-east-1
S3_USE_SSL: "false"
ZITADEL_DOMAIN: http://zitadel:8080
CORS_ALLOWED_ORIGINS: http://localhost:3000
volumes:
- ./server:/app
ports:
- "8000:8000"
depends_on:
postgres:
condition: service_healthy
minio:
condition: service_healthy
command: >
sh -c "
goose -dir ./migrations postgres \"$$DATABASE_URL\" up &&
./gopie serve
"
# Python Chat Server
gopie-chat:
build:
context: ./chat-server
dockerfile: Dockerfile
environment:
BACKEND_URL: http://gopie-server:8000
QDRANT_URL: http://qdrant:6333
OPENAI_API_KEY: ${OPENAI_API_KEY}
ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY}
LOG_LEVEL: INFO
volumes:
- ./chat-server:/app
ports:
- "8001:8001"
depends_on:
- gopie-server
- qdrant
# Web Frontend
gopie-web:
build:
context: ./web
dockerfile: Dockerfile
args:
NEXT_PUBLIC_BACKEND_URL: http://localhost:8000
NEXT_PUBLIC_CHAT_SERVER_URL: http://localhost:8001
NEXT_PUBLIC_APP_URL: http://localhost:3000
environment:
NEXT_PUBLIC_BACKEND_URL: http://localhost:8000
NEXT_PUBLIC_CHAT_SERVER_URL: http://localhost:8001
NEXT_PUBLIC_APP_URL: http://localhost:3000
NEXT_PUBLIC_ZITADEL_ISSUER: http://localhost:4455
ports:
- "3000:3000"
depends_on:
- gopie-server
- gopie-chat
volumes:
postgres_data:
minio_data:
qdrant_data:Production Configuration
# docker-compose.prod.yml
version: '3.8'
services:
gopie-server:
image: gopie/server:latest
environment:
ENVIRONMENT: production
LOG_LEVEL: WARN
DATABASE_URL: ${DATABASE_URL}
S3_ENDPOINT: ${S3_ENDPOINT}
S3_ACCESS_KEY: ${S3_ACCESS_KEY}
S3_SECRET_KEY: ${S3_SECRET_KEY}
deploy:
replicas: 3
resources:
limits:
cpus: '2'
memory: 2G
reservations:
cpus: '1'
memory: 1G
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
gopie-chat:
image: gopie/chat-server:latest
environment:
ENVIRONMENT: production
LOG_LEVEL: WARN
BACKEND_URL: http://gopie-server:8000
QDRANT_URL: ${QDRANT_URL}
deploy:
replicas: 2
resources:
limits:
cpus: '4'
memory: 4G
reservations:
cpus: '2'
memory: 2G
gopie-web:
image: gopie/web:latest
environment:
NODE_ENV: production
deploy:
replicas: 2
resources:
limits:
cpus: '1'
memory: 1G
reservations:
cpus: '0.5'
memory: 512M
nginx:
image: nginx:alpine
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf
- ./nginx/ssl:/etc/nginx/ssl
ports:
- "80:80"
- "443:443"
depends_on:
- gopie-web
- gopie-server
- gopie-chatNetwork Configuration
Custom Networks
# docker-compose.networks.yml
version: '3.8'
networks:
frontend:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/24
backend:
driver: bridge
ipam:
config:
- subnet: 172.21.0.0/24
data:
driver: bridge
ipam:
config:
- subnet: 172.22.0.0/24
services:
gopie-web:
networks:
- frontend
gopie-server:
networks:
- frontend
- backend
- data
gopie-chat:
networks:
- backend
- data
postgres:
networks:
- data
qdrant:
networks:
- data
minio:
networks:
- dataVolume Management
Named Volumes
volumes:
postgres_data:
driver: local
driver_opts:
type: none
o: bind
device: /data/gopie/postgres
minio_data:
driver: local
driver_opts:
type: none
o: bind
device: /data/gopie/minio
qdrant_data:
driver: local
driver_opts:
type: none
o: bind
device: /data/gopie/qdrantBackup Volumes
#!/bin/bash
# backup-volumes.sh
BACKUP_DIR="/backups/gopie/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"
# Backup PostgreSQL
docker run --rm \
-v gopie_postgres_data:/data \
-v "$BACKUP_DIR":/backup \
alpine tar czf /backup/postgres_data.tar.gz -C /data .
# Backup MinIO
docker run --rm \
-v gopie_minio_data:/data \
-v "$BACKUP_DIR":/backup \
alpine tar czf /backup/minio_data.tar.gz -C /data .
# Backup Qdrant
docker run --rm \
-v gopie_qdrant_data:/data \
-v "$BACKUP_DIR":/backup \
alpine tar czf /backup/qdrant_data.tar.gz -C /data .
echo "Backup completed: $BACKUP_DIR"Health Checks
Service Health Monitoring
# docker-compose.health.yml
services:
gopie-server:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
gopie-chat:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8001/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
gopie-web:
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/api/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60sHealth Check Script
#!/bin/bash
# check-health.sh
services=("gopie-server:8000" "gopie-chat:8001" "gopie-web:3000" "postgres:5432" "minio:9000" "qdrant:6333")
for service in "${services[@]}"; do
name="${service%%:*}"
port="${service##*:}"
if curl -f "http://localhost:$port/health" &>/dev/null; then
echo "$name is healthy"
else
echo "$name is unhealthy"
fi
doneSecurity Hardening
Docker Security
# Secure Dockerfile practices
FROM node:20-alpine AS base
# Run as non-root user
RUN addgroup -g 1001 -S nodejs
RUN adduser -S nextjs -u 1001
# Set secure permissions
RUN chmod -R 755 /app
RUN chown -R nextjs:nodejs /app
# Drop capabilities
RUN apk add --no-cache libcap
RUN setcap -r /usr/local/bin/node
USER nextjsSecrets Management
# docker-compose.secrets.yml
version: '3.8'
secrets:
db_password:
file: ./secrets/db_password.txt
openai_api_key:
file: ./secrets/openai_api_key.txt
jwt_secret:
file: ./secrets/jwt_secret.txt
services:
gopie-server:
secrets:
- db_password
- jwt_secret
environment:
DATABASE_PASSWORD_FILE: /run/secrets/db_password
JWT_SECRET_FILE: /run/secrets/jwt_secret
gopie-chat:
secrets:
- openai_api_key
environment:
OPENAI_API_KEY_FILE: /run/secrets/openai_api_keyPerformance Optimization
Build Optimization
# Multi-stage build with cache mounts
FROM golang:1.21-alpine AS builder
# Cache Go modules
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
go mod download
# Build with cache
RUN --mount=type=cache,target=/go/pkg/mod \
--mount=type=cache,target=/root/.cache/go-build \
go build -o gopie .Resource Limits
# docker-compose.limits.yml
services:
gopie-server:
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
reservations:
cpus: '0.5'
memory: 512M
ulimits:
nofile:
soft: 65536
hard: 65536Monitoring Integration
Prometheus & Grafana
# docker-compose.monitoring.yml
services:
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
ports:
- "9090:9090"
grafana:
image: grafana/grafana:latest
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards
- ./grafana/datasources:/etc/grafana/provisioning/datasources
environment:
GF_SECURITY_ADMIN_PASSWORD: admin
GF_USERS_ALLOW_SIGN_UP: false
ports:
- "3001:3000"
node-exporter:
image: prom/node-exporter:latest
ports:
- "9100:9100"
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
ports:
- "8080:8080"
volumes:
prometheus_data:
grafana_data:Deployment Scripts
Deploy Script
#!/bin/bash
# deploy.sh
set -e
ENVIRONMENT="${1:-development}"
echo "Deploying GoPie in $ENVIRONMENT mode..."
# Load environment variables
if [ -f ".env.$ENVIRONMENT" ]; then
export $(cat ".env.$ENVIRONMENT" | xargs)
fi
# Pull latest images
docker-compose pull
# Build images
docker-compose build --no-cache
# Run database migrations
docker-compose run --rm gopie-server goose -dir ./migrations postgres "$DATABASE_URL" up
# Start services
if [ "$ENVIRONMENT" = "production" ]; then
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
else
docker-compose up -d
fi
# Wait for services to be healthy
echo "Waiting for services to be healthy..."
sleep 30
# Check health
./check-health.sh
echo "Deployment complete!"Rollback Script
#!/bin/bash
# rollback.sh
set -e
BACKUP_TAG="${1:-latest-backup}"
echo "Rolling back to $BACKUP_TAG..."
# Stop current services
docker-compose down
# Restore from backup tags
docker-compose up -d \
--scale gopie-server=0 \
--scale gopie-chat=0 \
--scale gopie-web=0
# Start services with backup images
docker run -d --name gopie-server-backup gopie/server:$BACKUP_TAG
docker run -d --name gopie-chat-backup gopie/chat-server:$BACKUP_TAG
docker run -d --name gopie-web-backup gopie/web:$BACKUP_TAG
echo "Rollback complete!"Troubleshooting
Common Issues
-
Container fails to start:
# Check logs docker-compose logs gopie-server # Check container details docker inspect gopie-server -
Database connection issues:
# Test database connection docker-compose exec postgres psql -U gopie -d gopie -c "SELECT 1" # Check network connectivity docker-compose exec gopie-server ping postgres -
Permission issues:
# Fix volume permissions docker-compose exec gopie-server chown -R 1001:1001 /data
Debug Mode
# docker-compose.debug.yml
services:
gopie-server:
command: ["dlv", "debug", "--headless", "--listen=:2345", "--api-version=2"]
ports:
- "2345:2345"
environment:
CGO_ENABLED: 0Next Steps
- Set up Kubernetes Deployment
- Configure Monitoring
- Review Scaling Strategies
- Implement Security Best Practices