Docker Deployment¶
This guide covers deploying the Rust OAuth2 Server using Docker and Docker Compose.
Quick Start¶
The fastest way to get started with Docker:
# Clone the repository
git clone https://github.com/ianlintner/rust_oauth2_server.git
cd rust_oauth2_server
# Start with Docker Compose
docker-compose up -d
The server will be available at http://localhost:8080.
Docker Architecture¶
graph TB
subgraph DockerComposeStack[Docker Compose Stack]
subgraph OAuth2Service[OAuth2 Service]
OAuth2[OAuth2 Server Container]
end
subgraph DatabaseService[Database Service]
DB[(PostgreSQL Container)]
end
subgraph Observability
Jaeger[Jaeger Container]
Prometheus[Prometheus Container]
end
subgraph Networks
AppNet[app-network]
end
end
OAuth2 --> DB
OAuth2 --> Jaeger
Prometheus --> OAuth2
AppNet -.-> OAuth2
AppNet -.-> DB
AppNet -.-> Jaeger
AppNet -.-> Prometheus
style OAuth2 fill:#ff9800,color:#fff
style DB fill:#f3e5f5
style Jaeger fill:#fff9c4
style Prometheus fill:#e8f5e9
Building the Docker Image¶
Using the Provided Dockerfile¶
# Build the image
docker build -t rust_oauth2_server:latest .
# (Optional) Build with MongoDB backend support
# docker build --build-arg CARGO_FEATURES=mongo -t rust_oauth2_server:latest .
# Run the container
docker run -d \
-p 8080:8080 \
-e OAUTH2_DATABASE_URL=sqlite:oauth2.db \
-e OAUTH2_JWT_SECRET=your-secret-key \
--name oauth2_server \
rust_oauth2_server:latest
# (Optional) Run against MongoDB (requires image built with --build-arg CARGO_FEATURES=mongo)
# docker run -d \
# -p 8080:8080 \
# -e OAUTH2_DATABASE_URL=mongodb://mongo:27017/oauth2 \
# -e OAUTH2_JWT_SECRET=your-secret-key \
# --name oauth2_server \
# rust_oauth2_server:latest
Multi-Stage Dockerfile¶
The project includes an optimized multi-stage Dockerfile:
# Stage 1: Build
FROM rust:1.75-slim as builder
WORKDIR /app
# Copy manifests
COPY Cargo.toml Cargo.lock ./
# Copy source code
COPY src ./src
COPY migrations ./migrations
COPY templates ./templates
COPY static ./static
# Build release
RUN cargo build --release
# Stage 2: Runtime
FROM debian:bookworm-slim
# Install runtime dependencies
RUN apt-get update && apt-get install -y \
ca-certificates \
libssl3 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Copy binary from builder
COPY --from=builder /app/target/release/rust_oauth2_server .
# Copy runtime assets
COPY templates ./templates
COPY static ./static
COPY migrations ./migrations
# Expose port
EXPOSE 8080
# Run the server
CMD ["./rust_oauth2_server"]
Docker Compose Setup¶
Basic Configuration¶
Create docker-compose.yml:
version: '3.8'
services:
oauth2_server:
build: .
ports:
- "8080:8080"
environment:
OAUTH2_SERVER_HOST: 0.0.0.0
OAUTH2_SERVER_PORT: 8080
OAUTH2_DATABASE_URL: postgresql://oauth2:password@postgres:5432/oauth2_db
OAUTH2_JWT_SECRET: ${JWT_SECRET:-change-this-in-production}
OAUTH2_SESSION_KEY: ${SESSION_KEY:-change-this-in-production-must-be-64-chars}
RUST_LOG: info
depends_on:
postgres:
condition: service_healthy
networks:
- app-network
restart: unless-stopped
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: oauth2_db
POSTGRES_USER: oauth2
POSTGRES_PASSWORD: password
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- app-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U oauth2"]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
app-network:
driver: bridge
volumes:
postgres_data:
With Observability Stack¶
Complete setup with monitoring:
version: '3.8'
services:
oauth2_server:
build: .
ports:
- "8080:8080"
environment:
OAUTH2_SERVER_HOST: 0.0.0.0
OAUTH2_SERVER_PORT: 8080
OAUTH2_DATABASE_URL: postgresql://oauth2:password@postgres:5432/oauth2_db
OAUTH2_JWT_SECRET: ${JWT_SECRET}
OAUTH2_SESSION_KEY: ${SESSION_KEY}
OAUTH2_OTLP_ENDPOINT: http://jaeger:4317
RUST_LOG: info
depends_on:
- postgres
- jaeger
networks:
- app-network
restart: unless-stopped
postgres:
image: postgres:16-alpine
environment:
POSTGRES_DB: oauth2_db
POSTGRES_USER: oauth2
POSTGRES_PASSWORD: password
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- app-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U oauth2"]
interval: 5s
timeout: 5s
retries: 5
restart: unless-stopped
jaeger:
image: jaegertracing/all-in-one:latest
ports:
- "16686:16686" # Jaeger UI
- "4317:4317" # OTLP gRPC
environment:
COLLECTOR_OTLP_ENABLED: "true"
networks:
- app-network
restart: unless-stopped
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
networks:
- app-network
restart: unless-stopped
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
GF_SECURITY_ADMIN_PASSWORD: admin
volumes:
- grafana_data:/var/lib/grafana
networks:
- app-network
restart: unless-stopped
networks:
app-network:
driver: bridge
volumes:
postgres_data:
prometheus_data:
grafana_data:
Prometheus Configuration¶
Create prometheus.yml:
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'oauth2_server'
static_configs:
- targets: ['oauth2_server:8080']
metrics_path: '/metrics'
Environment Variables¶
Create .env file for Docker Compose:
# Server Configuration
OAUTH2_SERVER_HOST=0.0.0.0
OAUTH2_SERVER_PORT=8080
# Database
OAUTH2_DATABASE_URL=postgresql://oauth2:password@postgres:5432/oauth2_db
# Security (IMPORTANT: Change these!)
JWT_SECRET=your-secure-random-secret-minimum-32-characters
SESSION_KEY=your-secure-session-key-must-be-at-least-64-characters
# Token Expiration
OAUTH2_ACCESS_TOKEN_EXPIRATION=3600
OAUTH2_REFRESH_TOKEN_EXPIRATION=2592000
# Observability
OAUTH2_OTLP_ENDPOINT=http://jaeger:4317
RUST_LOG=info
# Social Login (Optional)
# OAUTH2_GOOGLE_CLIENT_ID=
# OAUTH2_GOOGLE_CLIENT_SECRET=
Database Migrations¶
Automatic Migrations¶
Run migrations automatically on startup:
services:
oauth2_server:
# ...
entrypoint: ["/bin/sh", "-c"]
command:
- |
# Run migrations
flyway migrate
# Start server
./rust_oauth2_server
Using Init Container¶
services:
migration:
image: flyway/flyway:10-alpine
command: migrate
volumes:
- ./migrations/sql:/flyway/sql
- ./flyway.conf:/flyway/conf/flyway.conf
networks:
- app-network
depends_on:
postgres:
condition: service_healthy
oauth2_server:
# ...
depends_on:
- migration
- postgres
Docker Commands¶
Build¶
# Build image
docker-compose build
# Build without cache
docker-compose build --no-cache
# Build specific service
docker-compose build oauth2_server
Run¶
# Start all services
docker-compose up -d
# Start specific service
docker-compose up -d oauth2_server
# View logs
docker-compose logs -f oauth2_server
# View all logs
docker-compose logs -f
Stop¶
# Stop all services
docker-compose down
# Stop and remove volumes
docker-compose down -v
# Stop specific service
docker-compose stop oauth2_server
Restart¶
# Restart all services
docker-compose restart
# Restart specific service
docker-compose restart oauth2_server
Status¶
# View running containers
docker-compose ps
# View resource usage
docker-compose stats
Health Checks¶
Add health checks to your services:
services:
oauth2_server:
# ...
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
Volume Management¶
Backup Database¶
# Backup PostgreSQL
docker-compose exec postgres pg_dump -U oauth2 oauth2_db > backup.sql
# Restore from backup
docker-compose exec -T postgres psql -U oauth2 oauth2_db < backup.sql
Persistent Data¶
Volumes ensure data persists across container restarts:
volumes:
postgres_data:
driver: local
# Or use named volume with specific driver
postgres_data:
driver: local
driver_opts:
type: none
device: /path/to/data
o: bind
Networking¶
Expose Ports¶
services:
oauth2_server:
ports:
- "8080:8080" # host:container
- "127.0.0.1:8080:8080" # bind to localhost only
Custom Network¶
networks:
frontend:
driver: bridge
backend:
driver: bridge
services:
oauth2_server:
networks:
- frontend
- backend
postgres:
networks:
- backend
Security¶
Run as Non-Root User¶
Update Dockerfile:
# Create non-root user
RUN useradd -m -u 1000 oauth2user
# Change ownership
RUN chown -R oauth2user:oauth2user /app
# Switch to non-root user
USER oauth2user
CMD ["./rust_oauth2_server"]
Secrets Management¶
Use Docker secrets:
version: '3.8'
services:
oauth2_server:
secrets:
- jwt_secret
- session_key
environment:
OAUTH2_JWT_SECRET_FILE: /run/secrets/jwt_secret
OAUTH2_SESSION_KEY_FILE: /run/secrets/session_key
secrets:
jwt_secret:
file: ./secrets/jwt_secret.txt
session_key:
file: ./secrets/session_key.txt
Production Deployment¶
Use HTTPS¶
Behind a reverse proxy (Nginx/Traefik):
services:
nginx:
image: nginx:alpine
ports:
- "443:443"
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./certs:/etc/nginx/certs
depends_on:
- oauth2_server
networks:
- app-network
oauth2_server:
# No ports exposed directly
expose:
- "8080"
networks:
- app-network
Resource Limits¶
services:
oauth2_server:
deploy:
resources:
limits:
cpus: '2'
memory: 2G
reservations:
cpus: '0.5'
memory: 512M
Restart Policy¶
services:
oauth2_server:
restart: unless-stopped
# or
restart: always
# or
restart: on-failure:3
Monitoring¶
Docker Stats¶
# View resource usage
docker stats oauth2_server
# Continuous monitoring
watch docker stats
Container Logs¶
# Follow logs
docker-compose logs -f oauth2_server
# Last 100 lines
docker-compose logs --tail=100 oauth2_server
# Logs since 1 hour ago
docker-compose logs --since 1h oauth2_server
Troubleshooting¶
Container Won't Start¶
# Check logs
docker-compose logs oauth2_server
# Inspect container
docker inspect oauth2_server
# Check configuration
docker-compose config
Database Connection Issues¶
# Check if database is running
docker-compose ps postgres
# Test connection
docker-compose exec oauth2_server nc -zv postgres 5432
# View database logs
docker-compose logs postgres
Permission Issues¶
# Check file ownership
docker-compose exec oauth2_server ls -la
# Fix permissions
sudo chown -R 1000:1000 ./data
Next Steps¶
- Kubernetes Deployment - Deploy to Kubernetes
- Production Guide - Production best practices
- Monitoring Setup - Set up monitoring
- Configuration Guide - Advanced configuration