Complete guide for deploying IngestKit with Docker and achieving zero-downtime schema updates.
Last updated: 2025-11-15
- Overview
- Quick Start
- Zero-Downtime Schema Updates
- Architecture
- Configuration
- Production Deployment
- Troubleshooting
IngestKit supports Docker-based deployment with:
- Zero-downtime rolling updates for schema changes
- Health checks for graceful container lifecycle management
- Automatic dependency management with Docker Compose
- Multi-stage builds for optimized image sizes
- Non-root containers for security
| Service | Image | Port | Purpose |
|---|---|---|---|
| API | ingestkit-api | 8080 | HTTP event ingestion |
| Consumer | ingestkit-consumer | 8081 | Kafka → PostgreSQL |
| PostgreSQL | postgres:18-alpine | 5433 | Event storage |
| Redpanda | redpanda:v23.3.5 | 19092 | Message broker |
# Build images and start all services
make docker-upOutput:
✓ Full stack started!
API: http://localhost:8080
Consumer Metrics: http://localhost:8081/metrics
PostgreSQL: localhost:5433
Redpanda: localhost:19092
Redpanda Console: http://localhost:8090
# Check health
curl http://localhost:8080/health
# Check consumer metrics
curl http://localhost:8081/health
# View logs
make docker-logscurl -X POST http://localhost:8080/v1/events/user_signup \
-H "Authorization: Bearer dev_key_1234567890" \
-H "Content-Type: application/json" \
-d '{
"user_id": "test_001",
"email": "test@example.com",
"signup_source": "web"
}'Traditional deployment:
- Change schema → regenerate code → rebuild binaries
- Stop services (downtime begins)
- Deploy new binaries
- Start services (downtime ends)
Result: 2-5 seconds of downtime per deployment
Docker rolling updates:
- Change schema → regenerate code → rebuild binaries → build Docker images
- Start new container with health check
- Wait for new container to be healthy
- Stop old container
Result: Zero downtime - service always available
# Single command for complete zero-downtime update
make docker-reloadWhat happens:
Step 1/5: Generating code from schema...
✓ Generated models, storage, consumer handlers
Step 2/5: Building binaries...
✓ Built API and consumer binaries
Step 3/5: Applying database migrations...
✓ Schema applied to PostgreSQL
Step 4/5: Building Docker images...
✓ Built ingestkit-api:latest
✓ Built ingestkit-consumer:latest
Step 5/5: Rolling update...
✓ API server reloaded (old: 1/1, new: 1/1)
✓ Consumer reloaded (old: 1/1, new: 1/1)
✓ Zero-downtime reload complete!
Services are running with new schema
Docker Compose up -d --no-deps --build:
- Builds new image with updated code
- Starts new container (e.g.,
api_2) - Waits for health check to pass
- Stops old container (e.g.,
api_1) - Removes old container
Health Check Configuration:
healthcheck:
test: ["CMD", "wget", "--spider", "http://localhost:8080/health"]
interval: 10s
timeout: 3s
retries: 3
start_period: 5sNew container must respond to /health within 5 seconds startup + 3 retries = ~20 seconds max before considered unhealthy.
Both API and consumer use multi-stage builds:
Dockerfile.api:
# Stage 1: Build
FROM golang:1.24-alpine AS builder
WORKDIR /build
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o api ./cmd/api
# Stage 2: Runtime
FROM alpine:latest
RUN apk add ca-certificates tzdata
COPY --from=builder /build/api .
COPY schema/events.yaml schema/events.yaml
EXPOSE 8080
CMD ["./api"]Benefits:
- Build tools (~500MB) not included in runtime image
- Final image: ~20MB vs ~500MB
- Faster deployments
- Smaller attack surface
Health checks enable:
- Graceful startup: Container marked "starting" until healthy
- Graceful shutdown: Old container receives SIGTERM, drains connections
- Rolling updates: New container healthy before old stops
- Load balancer integration: Unhealthy containers removed from pool
All services on ingestkit bridge network:
- Internal hostnames:
postgres,redpanda,api,consumer - External ports: 8080 (API), 8081 (metrics), 5433 (postgres), 19092 (redpanda)
Set in .env file:
# API Configuration
API_PORT=8080
RATE_LIMIT_RPS=20000
API_KEY_1=dev_key_1234567890:default
API_KEY_2=sk_test_tenant_alpha:tenant_alpha
ADMIN_SCHEMA_KEY=admin_secret_key_here
# Consumer Configuration
CONSUMER_WORKERS=4
CONSUMER_BATCH_SIZE=500
CONSUMER_BATCH_TIMEOUT_MS=20
# Database
POSTGRES_USER=ingestkit
POSTGRES_PASSWORD=ingestkit_dev
POSTGRES_DB=ingestkit
POSTGRES_PORT=5433
# Redpanda
REDPANDA_TOPIC=ingestkit.events
CONSUMER_GROUP_ID=ingestkit-consumer
# Scaling (optional)
API_REPLICAS=2
CONSUMER_REPLICAS=2Scale services horizontally:
# Scale API to 3 instances
docker-compose up -d --scale api=3
# Scale consumer to 2 instances
docker-compose up -d --scale consumer=2Note: Requires load balancer for API (not included in docker-compose.yml)
- Set production API keys (not
dev_key_*) - Set
ADMIN_SCHEMA_KEYfor schema push protection - Configure appropriate
RATE_LIMIT_RPS - Set
CONSUMER_WORKERSbased on load - Enable TLS termination (use reverse proxy like Nginx/Traefik)
- Set up monitoring (Prometheus scrapes
/metrics) - Configure log aggregation (stdout/stderr → logging service)
- Set up automated backups for PostgreSQL
- Configure DLQ alerting
- Test rolling updates in staging first
# Deploy stack
docker stack deploy -c docker-compose.yml ingestkit
# Update with zero downtime
docker service update --image ingestkit-api:v2 ingestkit_apiConvert docker-compose.yml to Kubernetes manifests:
# Using kompose
kompose convert -f docker-compose.yml
# Deploy
kubectl apply -f .For more complex Kubernetes deployments, we recommend using Helm (chart coming soon).
Check logs:
make docker-logs-api
make docker-logs-consumerCommon issues:
- Missing
.envfile → Runmake setup - Database not ready → Wait for
postgreshealth check - Port conflict → Change
API_PORTorPOSTGRES_PORTin.env
Symptoms: Container repeatedly restarting
Debug:
# Exec into container
docker exec -it ingestkit-api sh
# Test health endpoint manually
wget -O- http://localhost:8080/healthCommon causes:
- API not binding to
0.0.0.0(checkAPI_PORTenv) - Schema file missing in container
- Database connection failing
Symptoms: New container starts but old container doesn't stop
Check:
# View container status
docker-compose ps
# Check health status
docker inspect ingestkit-api | grep Health -A 10Solution:
# Force recreation
docker-compose up -d --force-recreate apiSymptoms: Events rejected after docker-reload
Verify schema version:
# Check running version
curl http://localhost:8080/schema | head -1
# Should show: version: "1.0" (or your version)
# Check consumer logs for schema version
make docker-logs-consumer | grep "schema versions"
# Should show: map[1.0:N] where N is event countSolution:
# Ensure schema-apply ran before docker-reload
make schema-apply
make docker-build
make docker-reloadSpeed up builds with BuildKit:
export DOCKER_BUILDKIT=1
export COMPOSE_DOCKER_CLI_BUILD=1
make docker-buildBuild for different architectures:
docker buildx build --platform linux/amd64,linux/arm64 \
-t ingestkit-api:latest -f Dockerfile.api .Set memory/CPU limits in docker-compose.yml:
api:
deploy:
resources:
limits:
cpus: '2'
memory: 2G
reservations:
cpus: '1'
memory: 1G# Build without cache
docker-compose build --no-cache
# View resource usage
docker stats
# Clean up unused images
docker system prune -a
# Export logs
docker-compose logs api > api.log
# Restart single service
docker-compose restart api
# Stop and remove volumes (⚠️ deletes data)
docker-compose down -v- Set up CI/CD for automated deployments
- Configure monitoring and alerting
- Implement blue-green deployment
- Set up database replication
- Configure TLS certificates