Configuration

Proxy settings

Configure Caddy reverse proxy for automatic HTTPS and load balancing.


Overview

Odysseus uses Caddy as a reverse proxy for web roles. Caddy provides:

  • Automatic HTTPS with Let's Encrypt
  • HTTP/2 and HTTP/3 support
  • Dynamic configuration via admin API
  • Zero-downtime routing updates

Basic configuration

proxy:
  hosts:
    - myapp.example.com
  app_port: 3000
  ssl: true
  ssl_email: admin@example.com

hosts

Domain names that route to your application. Caddy will obtain SSL certificates for each domain.

proxy:
  hosts:
    - myapp.example.com
    - www.myapp.example.com
    - api.myapp.example.com

app_port

The port your application listens on inside the container.

proxy:
  app_port: 3000

Common ports:

  • Rails: 3000
  • Node.js: 3000 or 8080
  • Python/Gunicorn: 8000
  • Go: 8080

SSL configuration

Automatic HTTPS

Enable automatic SSL with Let's Encrypt:

proxy:
  hosts:
    - myapp.example.com
  ssl: true
  ssl_email: admin@example.com

DNS configuration

Your domain must point to your server's IP address before deploying. Let's Encrypt validates domain ownership via HTTP.

Without SSL

For internal services or development:

proxy:
  hosts:
    - internal.local
  ssl: false

Health checks

Configure HTTP health checks for your application.

proxy:
  hosts:
    - myapp.example.com
  app_port: 3000
  healthcheck:
    path: /health
    interval: 10
    timeout: 5
    expect_status: 200  # Optional: specific status code or range

path

The endpoint Caddy checks to verify your app is healthy.

interval

Seconds between health checks. Default: 10.

timeout

Seconds to wait for a response. Default: 5.

expect_status

The HTTP status code expected from the health check. Default: any 2xx status.

You can specify:

  • A specific status code: 200, 301, 204
  • A status range: "2xx", "3xx"

This is useful for applications that return non-200 responses on their health endpoints:

# Ghost CMS redirects root to /ghost/
healthcheck:
  path: /
  expect_status: 301

# API that returns 204 No Content
healthcheck:
  path: /health
  expect_status: 204

# Accept any 2xx status (default behavior)
healthcheck:
  path: /health
  expect_status: "2xx"

Health check endpoint

Your application should expose a health check endpoint:

# Rails example
get '/health', to: proc { [200, {}, ['OK']] }
// Express example
app.get('/health', (req, res) => res.send('OK'))

How routing works

During deployment

  1. New container starts
  2. Odysseus waits for container health check
  3. New container added to Caddy upstream
  4. Traffic flows to both old and new containers
  5. Old container removed from upstream
  6. Old container gracefully drained
  7. Old container stopped

Load balancing

With multiple web servers:

servers:
  web:
    hosts:
      - app1.example.com
      - app2.example.com

Caddy load balances across all healthy containers using round-robin.


Advanced configuration

Multiple domains

Route different domains to the same application:

proxy:
  hosts:
    - myapp.example.com
    - www.myapp.example.com
    - app.myapp.example.com

All domains receive SSL certificates and route to the same containers.

Internal services

For services that don't need public access (like job workers), don't configure a proxy:

servers:
  web:
    hosts:
      - app.example.com
  jobs:
    hosts:
      - worker.example.com
    cmd: bundle exec sidekiq

proxy:
  hosts:
    - myapp.example.com
  app_port: 3000

Only the web role gets proxy configuration. The jobs role runs without public access.


Caddy management

Odysseus automatically deploys and manages Caddy as a Docker container (odysseus-caddy). You don't need to install Caddy manually on your servers.

On your first deploy, Odysseus will:

  1. Create the odysseus Docker network
  2. Start the Caddy container with ports 80, 443, and 2019 (admin API)
  3. Mount /var/lib/odysseus/caddy for persistent SSL certificates

Verify Caddy

Check if the Caddy container is running:

ssh your-server "docker ps | grep odysseus-caddy"
ssh your-server "curl -s localhost:2019/config/"

Certificate persistence

SSL certificates are stored in /var/lib/odysseus/caddy on the host. This ensures certificates persist across container restarts and aren't re-requested from Let's Encrypt.

Previous
Servers and roles