Deployment

Deploy ezAuth to production on a VPS with Caddy, PostgreSQL, and Redis.

Requirements

Stack

ComponentRole
CaddyReverse proxy with automatic TLS (Let's Encrypt)
UvicornASGI application server
PostgreSQLPrimary database
RedisCaching, rate limiting, session state
systemdProcess management

1. Install Dependencies

# PostgreSQL
sudo apt install postgresql

# Redis
sudo apt install redis

# Caddy
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy.gpg
echo "deb [signed-by=/usr/share/keyrings/caddy.gpg] https://dl.cloudsmith.io/public/caddy/stable/deb/debian any-version main" | sudo tee /etc/apt/sources.list.d/caddy.list
sudo apt update && sudo apt install caddy

2. Set Up the Database

sudo -u postgres createuser ezauth
sudo -u postgres createdb ezauth -O ezauth
sudo -u postgres psql -c "ALTER USER ezauth PASSWORD 'your-secure-password';"

3. Deploy the Application

# Copy files to the server
rsync -avz --exclude .git --exclude __pycache__ ./ user@server:/opt/ezauth/

# On the server: install Python dependencies
cd /opt/ezauth
python3 -m venv .venv
source .venv/bin/activate
pip install -e .

# Run migrations
alembic upgrade head

4. Configure Environment

Create /opt/ezauth/.env with your production settings:

DATABASE_URL=postgresql+asyncpg://ezauth:password@localhost:5432/ezauth
REDIS_URL=redis://localhost:6379/0
SES_REGION=us-east-1
[email protected]
[email protected]
SESSION_COOKIE_SECURE=true

5. Configure Caddy

Edit /etc/caddy/Caddyfile:

# Global options: on-demand TLS for customer custom domains
{
    on_demand_tls {
        ask http://127.0.0.1:8001/internal/domain-check
    }
}

# Primary API domain
api.ezauth.org {
    reverse_proxy 127.0.0.1:8001
}

# Catch-all for customer custom domains (CNAME → api.ezauth.org)
https:// {
    tls {
        on_demand
    }
    reverse_proxy 127.0.0.1:8001
}

When a customer sets a CNAME from their domain (e.g. auth.customer.com) to api.ezauth.org, Caddy will automatically provision a TLS certificate for it via Let's Encrypt. The ask endpoint ensures certificates are only issued for verified domains in the database.

sudo systemctl reload caddy

6. Create systemd Service

Create /etc/systemd/system/ezauth.service:

[Unit]
Description=EZAuth
After=network.target postgresql.service redis.service

[Service]
Type=simple
User=ezauth
WorkingDirectory=/opt/ezauth
EnvironmentFile=/opt/ezauth/.env
ExecStart=/opt/ezauth/.venv/bin/uvicorn ezauth.main:app --host 127.0.0.1 --port 8001
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable ezauth
sudo systemctl start ezauth

7. Verify

# Check the service is running
sudo systemctl status ezauth

# Check the API responds
curl https://auth.yourdomain.com/docs

Updating

# Sync new code
rsync -avz --exclude .git --exclude __pycache__ ./ user@server:/opt/ezauth/

# On the server
cd /opt/ezauth
source .venv/bin/activate
pip install -e .
alembic upgrade head
sudo systemctl restart ezauth