Skip to content

Installation Guide

fuomag9 edited this page Sep 26, 2026 · 9 revisions

Installation Guide

How to install and set up Caddy Proxy Manager.

Table of Contents

  1. Prerequisites
  2. Installation
  3. Initial Configuration
  4. Updating
  5. Uninstallation
  6. Docker Compose Customization
  7. Next Steps
  8. Troubleshooting

Prerequisites

You'll need:

  • 64-bit Linux, macOS, or Windows with WSL2
  • Docker 20.10+ and Docker Compose 2.0+
  • 500MB disk space
  • 512MB RAM (1GB recommended)
  • Ports 80, 443, and 3000 available

Check your Docker install:

docker --version
docker compose version

Should show Docker 24.0.0+ and Docker Compose v2.20.0+.


Installation

Step 1: Clone the Repository

git clone https://github.com/fuomag9/caddy-proxy-manager.git
cd caddy-proxy-manager

Step 2: Create Environment File

cp .env.example .env

Step 3: Configure Environment Variables

.env.example leaves SESSION_SECRET, ADMIN_PASSWORD and CLICKHOUSE_PASSWORD empty, and docker compose refuses to start until you fill them in (since v1.13.1; the example SESSION_SECRET and ADMIN_PASSWORD values older .env.example files shipped are now rejected).

Generate the secrets:

openssl rand -base64 32   # for SESSION_SECRET
openssl rand -base64 32   # for CLICKHOUSE_PASSWORD

Then edit .env directly:

nano .env
# or
vi .env
SESSION_SECRET=<first generated value>
ADMIN_USERNAME=admin
ADMIN_PASSWORD=<choose-your-own: 12+ chars, upper, lower, digit, symbol>

# Optional: Set custom base URL
BASE_URL=http://localhost:3000

Required variables:

  • SESSION_SECRET - 32+ characters, generated with openssl rand -base64 32
  • ADMIN_USERNAME - Admin login username: 3–255 characters from A-Z a-z 0-9 _ . @ -
  • ADMIN_PASSWORD - 12+ chars with uppercase, lowercase, numbers, and special characters

Don't copy a secret or password from the documentation: the web container refuses the known example values (placeholder SESSION_SECRETs and example admin passwords from earlier READMEs and .env.example files).

Analytics (enabled by default):

# Analytics (enabled by default)
COMPOSE_PROFILES=clickhouse
CLICKHOUSE_PASSWORD=<second generated value>

# GeoIP updates (optional, for geo blocking — add alongside clickhouse)
# COMPOSE_PROFILES=clickhouse,geoipupdate
# GEOIPUPDATE_ACCOUNT_ID=your-account-id
# GEOIPUPDATE_LICENSE_KEY=your-license-key

ClickHouse is enabled via the clickhouse Docker Compose profile. Removing it from COMPOSE_PROFILES disables analytics. The web container starts normally either way — the Analytics page will show a banner when ClickHouse is not running. CLICKHOUSE_PASSWORD is only required while the clickhouse profile is active.

Also back up SESSION_SECRET together with the database: it encrypts the secrets CPM stores (DNS provider credentials, OAuth client secrets, certificate and CA private keys, instance sync tokens), which cannot be decrypted without it.

See Environment Variables Reference for complete variable documentation.

Step 4: Secure the Environment File

chmod 600 .env

This prevents other users from reading your secrets.

Step 5: Start the Containers

docker compose up -d

This will:

  1. Pull/build the images (first run takes 2-5 minutes)
  2. Create Docker volumes for data persistence
  3. Start the web and Caddy containers
  4. Initialize the database
  5. Create the admin user

Step 6: Verify Containers are Running

docker ps

Expected output:

CONTAINER ID   IMAGE                                    STATUS
xxxxx          ghcr.io/fuomag9/...-web:latest          Up (healthy)
xxxxx          ghcr.io/fuomag9/...-caddy:latest        Up (healthy)

Check logs if containers aren't healthy:

docker compose logs web
docker compose logs caddy

Initial Configuration

Access the Web UI

Open your browser and navigate to:

http://localhost:3000/login

If you set a custom BASE_URL, use that instead.

First Login

Development mode (NODE_ENV=development) uses default credentials:

  • Username: admin
  • Password: admin

Note: These defaults are blocked in production for security. In production, use the credentials from your .env file:

  • Username: Value of ADMIN_USERNAME
  • Password: Value of ADMIN_PASSWORD

To log in:

  1. Enter your credentials
  2. Click "Sign in"
  3. You should be redirected to the dashboard

Change Password (Recommended)

After first login, change your password from the profile page:

  1. Click the user icon in the top right
  2. Select "Profile"
  3. Update your password
  4. Save changes

The new password must meet the same policy (12+ characters, upper- and lowercase, a digit and a special character).

Since v1.13.1, saving it signs out your other sessions, and a password changed here is kept across restarts: ADMIN_USERNAME and ADMIN_PASSWORD are applied only when the admin account is created and whenever you change them in .env. To recover a lost admin password, set ADMIN_PASSWORD to a new value and run docker compose up -d. See Environment Variables Reference.

Configure Settings

Navigate to Settings to configure:

  1. General Settings

    • Primary domain for Caddy
    • ACME email for Let's Encrypt
  2. DNS Providers (Optional)

  3. Metrics (Optional)

    • Enable Prometheus metrics
    • Set metrics port (default 9090)
  4. Logging (Optional)

    • Enable access logging
    • Choose log format (json/console)

Updating

Update to Latest Version

cd caddy-proxy-manager
git pull
docker compose pull
docker compose up -d

This will:

  1. Pull latest code changes
  2. Download new Docker images
  3. Restart containers with new versions
  4. Run database migrations automatically

Use docker compose up -d rather than docker compose restart after any .env change; restart does not re-read .env.

Upgrading from v1.13.0

  • Sign-in usernames are no longer generated from email addresses. The withdrawn v1.13.0 release could give an account a sign-in username made from its email address (alice+cpm@example.com → alice-cpm@example.com, with -2, -3, … added on a collision) when the account was created or its password or profile changed, and on every start for each account with a password whose stored username the login page could not use. Such a username can be somebody else's address. v1.13.1 removes this and leaves stored usernames as they are.
    • Before upgrading, save the lines Gave user <id> the sign-in username <username> from the web container log (docker compose logs web | grep "Gave user"); recreating the container usually discards its log. Review those accounts' usernames on the Users page (edit → Username).
    • Usernames generated outside startup were not logged. After upgrading, v1.13.1 logs a warning on every start, without changing anything, for each username that is an email address other than the account's own or that also reaches another account (Sign-in username "…" of user <id> …). See Troubleshooting#sign-in-username-warnings-on-startup.
    • Accounts without a usable username whose email address is not a valid username, or is already used by another account, now stay without one until an administrator sets it; their Profile page says so. See Feature Guide User Management#sign-in-usernames.

Upgrading from v1.12.0 or Earlier

Check these before upgrading to v1.13.1 or later. The Upgrade Notes in the README have the full list.

  • Example secrets are rejected. The web container refuses to start with a placeholder SESSION_SECRET (including the old .env.example value your-secure-session-secret-here-min-32-chars) or an example ADMIN_PASSWORD from an earlier README or .env.example. Generate a new secret; secrets stored under the placeholder are re-encrypted automatically on the next start.
  • New variables. SESSION_SECRET_PREVIOUS, FORWARD_AUTH_ALLOWED_PORTS, TRUSTED_CLIENT_IP_HEADER and INSTANCE_SYNC_TIMEOUT_MS are optional. The updated docker-compose.yml passes them to the web container; if you maintain your own compose file, add them there. See Environment Variables Reference.
  • Forward auth on a non-standard port. If browsers reach protected sites on a port other than 80/443 (e.g. Caddy published as 8443:443), set FORWARD_AUTH_ALLOWED_PORTS=8443, or portal logins and existing forward-auth sessions on that port stop working.
  • Forward auth behind a CDN. Portal login limits no longer trust a client-sent X-Real-IP; behind a CDN, set TRUSTED_CLIENT_IP_HEADER (see Security Configuration).
  • Instance sync: upgrade slaves before, or together with, the master. Proxies in front of a slave must also pass GET on /api/instances/sync. CA private keys are no longer synced, so slaves cannot issue client certificates. See Feature Guide Instance Sync#upgrading-from-v1120-or-earlier.
  • WAF custom directives that read files, run programs, switch the WAF off, cannot be parsed or reuse a rule id are no longer sent to Caddy; the web log lists them. See Feature Guide WAF.
  • Host placeholders are literal. {env.*}, {system.*} and {file.*} in default responses, error pages, path-block bodies and redirect rule targets are sent as written; request placeholders such as {http.request.uri} still expand.
  • Admin password. On the first start, a stored admin password that differs from ADMIN_PASSWORD (and is not admin or a documented example) is kept, since it was probably changed in the UI, and a warning is logged. Change ADMIN_PASSWORD again and run docker compose up -d to force it.
  • Sign-in usernames. The login page signs in by username only, ignoring case. Accounts whose stored username it cannot use (e.g. an email containing +, or a mixed-case username) are marked no sign-in username on the Users page. Since v1.13.1 CPM does not make one up from the email address: set one under edit → Username, or, when the account's own email address is a valid username, the user changes their password once after signing in another way (OAuth). Tell those users their username. See Feature Guide User Management#sign-in-usernames.
  • Database file permissions. The database files lose their world permission bits on startup; a host backup job running as an unrelated user can no longer read them (see Rootless Docker Operation).

Update with Rebuild

If there are Dockerfile changes:

cd caddy-proxy-manager
git pull
docker compose up --build -d

Verify Update

docker compose logs web | grep "version"

Rollback (if needed)

git checkout <previous-commit>
docker compose up --build -d

After rolling back from v1.13.1 or later to v1.12.0 or earlier, that version cannot use the CA private keys the newer release encrypted, so issuing client certificates fails there. With instance sync, reset a downgraded slave's sync key pin on the master, or its syncs fail with "Sync key request failed with HTTP 405".

Do not roll back to v1.13.0: it generates sign-in usernames from email addresses again on startup (see Upgrading from v1.13.0).


Uninstallation

Stop and Remove Containers

cd caddy-proxy-manager
docker compose down

Remove Volumes (Deletes All Data)

Warning: This deletes all proxy configurations, certificates, and database.

docker compose down -v

Remove Images

docker rmi ghcr.io/fuomag9/caddy-proxy-manager-web:latest
docker rmi ghcr.io/fuomag9/caddy-proxy-manager-caddy:latest

Remove Data Directories

rm -rf data caddy-data caddy-config caddy-logs

Remove Repository

cd ..
rm -rf caddy-proxy-manager

Docker Compose Customization

Docker Compose Profiles

Optional components are controlled via Docker Compose profiles set in the COMPOSE_PROFILES environment variable:

Profile What it starts Notes
clickhouse ClickHouse analytics database Enabled by default in .env.example
geoipupdate GeoIP database updater Required for geo blocking by country/ASN

To enable multiple profiles, separate them with a comma:

COMPOSE_PROFILES=clickhouse,geoipupdate

To disable a profile, remove it from COMPOSE_PROFILES. The rest of the stack (web and Caddy containers) starts normally regardless of which profiles are active.


Custom Ports

Edit docker-compose.yml to change ports:

services:
  web:
    ports:
      - "8080:3000"  # Change external port to 8080

  caddy:
    ports:
      - "8000:80"    # HTTP on port 8000
      - "8443:443"   # HTTPS on port 8443

If forward-auth protected sites are reached on these ports, list them in .env as FORWARD_AUTH_ALLOWED_PORTS=8000,8443 and run docker compose up -d (since v1.13.1); otherwise portal logins and forward-auth sessions on those ports are refused. If you change the web UI port, update BASE_URL to match.

Custom Volume Paths

services:
  web:
    volumes:
      - /custom/path/data:/app/data

  caddy:
    volumes:
      - /custom/path/caddy-data:/data
      - /custom/path/caddy-config:/config

Rootless Operation

To run as your host user (avoid permission issues):

# Add to .env
PUID=1000  # Your UID (find with: id -u)
PGID=1000  # Your GID (find with: id -g)

# Rebuild containers
docker compose up --build -d

See Rootless Docker Operation for detailed guide.

Expose Metrics

Uncomment in docker-compose.yml:

services:
  caddy:
    ports:
      - "80:80"
      - "443:443"
      - "9090:9090"  # Metrics endpoint

Then enable metrics in Settings page.

External Caddy Instance

To use external Caddy (not recommended):

services:
  web:
    environment:
      CADDY_API_URL: "http://your-caddy-host:2019"
    # Remove depends_on for caddy

  # Remove or comment out caddy service

Network Configuration

To use existing Docker network:

networks:
  caddy-network:
    external: true
    name: your-existing-network

Build from Source

services:
  web:
    build:
      context: .
      dockerfile: docker/web/Dockerfile
    # Remove 'image' line to always build locally

Next Steps

After installation:

  1. Configure a DNS Provider (Optional)

    • See DNS Provider Configuration
    • Required for wildcard certificates
    • Supports Cloudflare, Route 53, DigitalOcean, Hetzner, and 8 more
  2. Set up OAuth (Optional)

  3. Create Your First Proxy Host

  4. Review Security Settings

  5. Enable Audit Logging

    • Navigate to Settings → Logging
    • Enable access logs for debugging

Troubleshooting

Container Won't Start

Check logs:

docker compose logs web
docker compose logs caddy

Common issues:

  • Missing required environment variables (docker compose reports ERROR - SESSION_SECRET is required or ERROR - ADMIN_PASSWORD is required)
  • A placeholder SESSION_SECRET or an example ADMIN_PASSWORD copied from the documentation
  • Port conflicts (80, 443, 3000 in use)
  • Permission denied (try rootless mode)

Login Issues

Invalid credentials:

  • Verify .env file has correct values
  • Check docker logs: docker compose logs web
  • Ensure containers were recreated after changing .env (docker compose up -d; restart keeps the old values)
  • If the admin password was changed in the UI, ADMIN_PASSWORD no longer works; see Troubleshooting#login-fails-with-correct-credentials

Rate limited:

  • Wait 15 minutes (default block duration)
  • Or restart containers: docker compose restart web

Database Migration Errors

Reset database (deletes all data):

docker compose down
rm -rf data/caddy-proxy-manager.db*
docker compose up -d

Permission Denied Errors

Solution: Set PUID/PGID to match your host user:

PUID=1000 PGID=1000 docker compose up --build -d

See Troubleshooting for more solutions.


Related Documentation

Clone this wiki locally