Repository navigation
Installation Guide
How to install and set up Caddy Proxy Manager.
- Prerequisites
- Installation
- Initial Configuration
- Updating
- Uninstallation
- Docker Compose Customization
- Next Steps
- Troubleshooting
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 versionShould show Docker 24.0.0+ and Docker Compose v2.20.0+.
git clone https://github.com/fuomag9/caddy-proxy-manager.git
cd caddy-proxy-managercp .env.example .env.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_PASSWORDThen edit .env directly:
nano .env
# or
vi .envSESSION_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:3000Required variables:
-
SESSION_SECRET- 32+ characters, generated withopenssl rand -base64 32 -
ADMIN_USERNAME- Admin login username: 3–255 characters fromA-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-keyClickHouse 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.
chmod 600 .envThis prevents other users from reading your secrets.
docker compose up -dThis will:
- Pull/build the images (first run takes 2-5 minutes)
- Create Docker volumes for data persistence
- Start the web and Caddy containers
- Initialize the database
- Create the admin user
docker psExpected 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 caddyOpen your browser and navigate to:
http://localhost:3000/login
If you set a custom BASE_URL, use that instead.
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:
- Enter your credentials
- Click "Sign in"
- You should be redirected to the dashboard
After first login, change your password from the profile page:
- Click the user icon in the top right
- Select "Profile"
- Update your password
- 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.
Navigate to Settings to configure:
-
General Settings
- Primary domain for Caddy
- ACME email for Let's Encrypt
-
DNS Providers (Optional)
- Configure DNS-01 challenge providers for wildcard certs
- See DNS Provider Configuration for details
-
Metrics (Optional)
- Enable Prometheus metrics
- Set metrics port (default 9090)
-
Logging (Optional)
- Enable access logging
- Choose log format (json/console)
cd caddy-proxy-manager
git pull
docker compose pull
docker compose up -dThis will:
- Pull latest code changes
- Download new Docker images
- Restart containers with new versions
- Run database migrations automatically
Use docker compose up -d rather than docker compose restart after any .env change; restart does not re-read .env.
-
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.
- Before upgrading, save the lines
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.examplevalueyour-secure-session-secret-here-min-32-chars) or an exampleADMIN_PASSWORDfrom 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_HEADERandINSTANCE_SYNC_TIMEOUT_MSare optional. The updateddocker-compose.ymlpasses 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), setFORWARD_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, setTRUSTED_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
GETon/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 notadminor a documented example) is kept, since it was probably changed in the UI, and a warning is logged. ChangeADMIN_PASSWORDagain and rundocker compose up -dto 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).
If there are Dockerfile changes:
cd caddy-proxy-manager
git pull
docker compose up --build -ddocker compose logs web | grep "version"git checkout <previous-commit>
docker compose up --build -dAfter 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).
cd caddy-proxy-manager
docker compose downWarning: This deletes all proxy configurations, certificates, and database.
docker compose down -vdocker rmi ghcr.io/fuomag9/caddy-proxy-manager-web:latest
docker rmi ghcr.io/fuomag9/caddy-proxy-manager-caddy:latestrm -rf data caddy-data caddy-config caddy-logscd ..
rm -rf caddy-proxy-managerOptional 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,geoipupdateTo 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.
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 8443If 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.
services:
web:
volumes:
- /custom/path/data:/app/data
caddy:
volumes:
- /custom/path/caddy-data:/data
- /custom/path/caddy-config:/configTo 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 -dSee Rootless Docker Operation for detailed guide.
Uncomment in docker-compose.yml:
services:
caddy:
ports:
- "80:80"
- "443:443"
- "9090:9090" # Metrics endpointThen enable metrics in Settings page.
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 serviceTo use existing Docker network:
networks:
caddy-network:
external: true
name: your-existing-networkservices:
web:
build:
context: .
dockerfile: docker/web/Dockerfile
# Remove 'image' line to always build locallyAfter installation:
-
Configure a DNS Provider (Optional)
- See DNS Provider Configuration
- Required for wildcard certificates
- Supports Cloudflare, Route 53, DigitalOcean, Hetzner, and 8 more
-
Set up OAuth (Optional)
- See OAuth Authentication Setup
- Configure SSO with Authentik, Keycloak, etc.
-
Create Your First Proxy Host
- See Feature Guide Proxy Hosts
- Configure reverse proxy to your services
-
Review Security Settings
- See Security Configuration
- Production security best practices
-
Enable Audit Logging
- Navigate to Settings → Logging
- Enable access logs for debugging
Check logs:
docker compose logs web
docker compose logs caddyCommon issues:
- Missing required environment variables (
docker composereportsERROR - SESSION_SECRET is requiredorERROR - ADMIN_PASSWORD is required) - A placeholder
SESSION_SECRETor an exampleADMIN_PASSWORDcopied from the documentation - Port conflicts (80, 443, 3000 in use)
- Permission denied (try rootless mode)
Invalid credentials:
- Verify
.envfile has correct values - Check docker logs:
docker compose logs web - Ensure containers were recreated after changing
.env(docker compose up -d;restartkeeps the old values) - If the admin password was changed in the UI,
ADMIN_PASSWORDno longer works; see Troubleshooting#login-fails-with-correct-credentials
Rate limited:
- Wait 15 minutes (default block duration)
- Or restart containers:
docker compose restart web
Reset database (deletes all data):
docker compose down
rm -rf data/caddy-proxy-manager.db*
docker compose up -dSolution: Set PUID/PGID to match your host user:
PUID=1000 PGID=1000 docker compose up --build -dSee Troubleshooting for more solutions.
- Environment Variables Reference - All configuration variables
- Security Configuration - Production security
- Rootless Docker Operation - Non-root containers
- Troubleshooting - Common issues