Docker Deployment¶
This guide covers deploying the Rust OAuth2 Server using Docker and Docker Compose.
Prebuilt image (Docker Hub)¶
If you want to run the server without compiling, use the prebuilt Docker Hub image.
See: Docker Hub Image
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?mode=rwc \
-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