Skip to content

rhad00/CaddyManager

Repository files navigation

CaddyManager

License GitHub issues GitHub stars Docker Pulls

CaddyManager is the modern, hassle-free way to manage your reverse proxy. Built on the powerful Caddy Server, it gives you a beautiful UI to manage domains, SSL certificates, and complex headers without touching a single config file.

Stop debugging Nginx syntax. Start deploying services.

CaddyManager Dashboard

Note

This project is currently in Alpha. While we use it daily, please test thoroughly in your environment.

✨ Why CaddyManager?

🔌 "One-Click" Service Templates

Deploying complex apps like Authelia, Keycloak, or Nextcloud? Forget about manually configuring X-Forwarded-* headers or debugging infinite redirect loops.

CaddyManager includes battle-tested templates for these services. Just select "Authelia" from the dropdown, and we apply the perfect configuration automatically.

  • Supported Templates: Authelia, Keycloak, Amazon S3 (MinIO/Ceph), Nextcloud, Cloudflare Tunnel, Grafana, Kibana.

🔒 Zero-Config Cloudflare DNS Challenges

Want wildcard certificates or need to secure internal services without opening port 80?

Just provide your Cloudflare API Token. CaddyManager automatically configures Caddy's DNS-01 challenge.

  • No plugins to install.
  • No scripts to run.
  • It just works.

🛡️ Enterprise-Grade Security

Security shouldn't be optional. Enable industry-standard security headers with a single toggle:

  • HSTS (Strict-Transport-Security)
  • CSP (Content-Security-Policy)
  • X-Frame-Options & X-Content-Type-Options
  • IP Filtering (Allow/Block lists) & Rate Limiting

💾 Automatic Backups

Your configuration is precious. CaddyManager automatically backs up your proxy settings, routes, and certificates.

  • Auto-Backup: Scheduled backups of your entire config.
  • Instant Restore: Rollback to any previous state directly from the UI.
  • Portable: Export your config and move to a new server in seconds.

🐳 Docker Auto-Discovery (NEW!)

Deploy containers and get automatic reverse proxy configuration. No manual setup needed!

  • Label-Based Config: Add labels to your containers, get instant proxying.
  • Auto-Sync: Containers start/stop? Proxies update automatically.
  • Template Support: Auto-apply service templates via labels.
  • Zero Touch: Perfect for development environments and dynamic deployments.
docker run -d \
  --label caddymanager.enable=true \
  --label caddymanager.domain=myapp.local \
  --label caddymanager.port=3000 \
  --network caddy_net \
  my-awesome-app

That's it! Your app is now reverse proxied with SSL at myapp.local.

🚀 Features Overview

Feature Description
User Management JWT + httpOnly cookie auth, role-based access (Admin/ReadOnly), brute-force protection, account lockout.
Two-Factor Authentication TOTP-based 2FA with QR code setup, backup codes, enforced per-user.
API Key Authentication Long-lived API keys with scoped permissions for CI/CD and automation.
Proxy Management Multi-domain support, auto-SSL (Let's Encrypt), custom certs, HTTP→HTTPS redirects.
Load Balancing Multiple upstreams per proxy with round-robin, least-connections, IP-hash, weighted strategies.
Health Checks Active TCP/HTTP upstream health monitoring with automatic failover.
Access Log Viewer Live SSE log tail + historical search with domain/status code filters.
Alerting & Notifications Certificate expiry, upstream failure, and error-rate alerts via email, Slack, Discord, or webhook.
Security Headers One-click HSTS, CSP, X-Frame-Options, IP filtering, rate limiting.
Service Templates Battle-tested header/middleware templates for Authelia, Keycloak, Nextcloud, Grafana, and more.
Custom Template Creator Visual template builder — add headers by row, pick from presets, edit middleware JSON.
Docker Auto-Discovery Automatic proxy creation from container labels. Zero-config deployments.
Git Integration & GitOps Auto-commit config changes to Git; pull-from-Git GitOps mode. AES-256 token encryption.
Backups Scheduled backups, one-click restore, AES-256-GCM encryption, configurable retention policy.
OpenAPI Docs Interactive Swagger UI at /api/docs.
Metrics & Audit Logs Metrics snapshots, full audit trail for all admin actions.

How It Works

Initial Setup and Caddyfile

The application uses a base Caddyfile for initial startup that:

  • Configures the Admin API endpoint on port 2019 (accessible only from internal network)
  • Disables automatic HTTPS redirects initially
  • Sets up file system storage for certificates and data
  • Provides default placeholder responses on ports 80 and 443

On startup, the CaddyService:

  1. Checks for an existing configuration backup
  2. If a backup exists, loads it
  3. If no backup exists but there are proxies in the database, rebuilds the configuration
  4. If neither exists, uses the default Caddyfile configuration

Template System

The application includes pre-configured templates for common services:

  • Authelia (Authentication server)
  • Keycloak (Identity management)
  • Amazon S3 compatible services (MiniIO, Ceph RadosGW) (Storage service)
  • Nextcloud (Self-hosted productivity)
  • Cloudflare Tunnel
  • Grafana (Monitoring platform)
  • Kibana/Elastic (Dashboard)

Each template includes:

  • Predefined headers for proper service functionality
  • Middleware configurations (if required)
  • Specific routing rules

Templates can be applied to proxies to automatically configure:

  • Request/response headers
  • Authentication settings
  • Rate limiting
  • IP filtering
  • Path rewrites

Header Management

Headers are managed through a flexible system that supports:

  • Request and response header types
  • Dynamic values using Caddy placeholders
  • Template-based header inheritance
  • Per-proxy custom headers

Example header configurations:

  1. Authentication header from Authelia template:
{
  header_type: 'request',
  header_name: 'x-original-uri',
  header_value: '{http.request.uri}'
}
  1. Security header (automatically applied when security headers are enabled):
{
  header_type: 'response',
  header_name: 'Strict-Transport-Security',
  header_value: 'max-age=31536000; includeSubDomains; preload',
  enabled: true
}

🚀 Features

Core Features

  • Authentication & User Management

    • JWT stored in httpOnly cookies + localStorage fallback for SSE streams
    • Role-based access control (Admin, Read-only)
    • Brute-force protection with account lockout
    • Initial admin setup automation
    • Password reset via email (SMTP)
  • Two-Factor Authentication (TOTP)

    • Time-based one-time passwords (RFC 6238)
    • QR code setup wizard + optional manual key entry
    • 10 single-use backup codes generated on activation
    • Two-step login flow with short-lived challenge token
    • Enable/disable per user from the "My Account" tab
  • API Key Authentication

    • Create long-lived API keys for automation and CI/CD
    • Scoped permissions: read, write, admin
    • Optional expiry date and last-used tracking
    • SHA-256 hashed; raw key shown only once at creation
    • Pass via X-API-Key header in any API request
  • Proxy Host Management

    • Multi-domain support
    • Automatic SSL via Let's Encrypt (ACME)
    • Cloudflare DNS-01 challenge (wildcard certs, internal services)
    • Custom SSL certificate management
    • HTTP → HTTPS redirection
    • Compression options (gzip/zstd)
    • WebSocket support
  • Load Balancing & Health Checks

    • Multiple upstream URLs per proxy
    • Strategies: round_robin, least_conn, ip_hash, random, weighted
    • Active HTTP/TCP health checks with configurable interval, timeout, thresholds
    • Automatic failover to healthy upstreams
  • Header & Middleware Configuration

    • Custom header injection (request/response)
    • One-click security headers:
      • Strict-Transport-Security (HSTS)
      • X-Content-Type-Options
      • X-Frame-Options
      • Content-Security-Policy
      • Referrer-Policy
      • Permissions-Policy
    • Rate limiting middleware
    • IP filtering (allow/block lists)
    • Basic authentication
    • Path-based routing

Advanced Features

  • Access Log Viewer

    • Caddy structured JSON access logs auto-configured on first proxy creation
    • Live tail mode via Server-Sent Events (SSE) stream
    • Historical search with filters: domain, status code, IP, time range
    • Log stats endpoint: total requests, error rate, top domains
    • Accessible from the "Access Logs" tab in the Dashboard
  • Alerting & Notifications

    • Notification channels: Email (SMTP), Slack webhook, Discord webhook, generic webhook
    • Alert rule types: certificate expiry, upstream down, error rate, no traffic
    • Configurable threshold and cooldown period per rule
    • Proxy-specific or global rules
    • Manual "run now" trigger for testing
    • Automatic evaluation every 15 minutes
    • Accessible from the "Alerts" tab in the Dashboard
  • Service Templates

    • Predefined templates:
      • Authelia (authentication server)
      • Keycloak (identity management)
      • Amazon S3-compatible storage (MinIO, Ceph RadosGW)
      • Nextcloud (self-hosted productivity)
      • Cloudflare Tunnel
      • Grafana (monitoring platform)
      • Kibana/Elastic (dashboards)
    • Custom Template Creator ✨ — visual builder with per-row headers, preset dropdown, and middleware JSON editor
    • Create, edit, and delete templates from the UI (admin-only)
    • (Planned: import/export templates as portable JSON files)
  • Docker Auto-Discovery

    • Automatic service detection from Docker containers
    • Label-based proxy configuration
    • Auto-create/destroy proxies on container start/stop
    • Template auto-application via labels
    • Real-time event monitoring
    • Configurable auto-removal of stopped containers
    • Periodic reconciliation for cleanup
  • Git Integration & GitOps

    • Auto-commit all config changes to GitHub, GitLab, Gitea, or Bitbucket
    • GitOps mode: pull config from Git and auto-apply on interval
    • AES-256-GCM encryption for stored access tokens
    • Export to proxies.json, proxies.yaml, caddy.json
    • Full audit trail with diffs per change
    • Rollback to any commit (safety backup branch created automatically)
    • Customizable commit message templates
  • Backup & Restore

    • Scheduled automatic backups (every 24h)
    • One-click restore from any backup
    • AES-256-GCM encrypted backups ✨ — set BACKUP_ENCRYPTION_KEY to encrypt all backup files at rest
    • Retention policy ✨ — BACKUP_RETENTION_DAYS auto-deletes old auto-backups (default: 30 days)
    • Caddy config backup before every change for instant rollback
    • (Planned: SSL/certificate backup)
    • (Planned: cloud storage upload — S3/GCS/Azure)
  • Monitoring & Observability

    • System metrics snapshots (stored every 60 min)
    • Metrics dashboard with charts
    • Full audit log for all admin actions
    • Interactive OpenAPI / Swagger UI ✨ at /api/docs
    • (Planned: WAF integration — CrowdSec / ModSecurity)
    • (Planned: Prometheus metrics export endpoint)

Implementation Details

Proxy Management

  • Each proxy is stored in the database with:
    • Domain configuration
    • SSL settings
    • Upstream URL
    • Security headers configuration
    • Associated headers and middleware
  • Changes trigger automatic Caddy configuration updates via Admin API
  • Configuration backups are maintained for reliability

Configuration Persistence

  • Configurations are stored in both the database and Caddy
  • Automatic backup system maintains config_backups/caddy_config_backup.json which can be mounted as local folder in docker or in a volume
  • Configuration is rebuilt from database on service restart
  • Handles both HTTP and HTTPS proxies with proper SSL termination (ACME Let's Encrypt without user intervention)

Template Implementation

Templates simplify service configuration through:

  1. Predefined header sets for common services
  2. Middleware configurations (rate limiting, auth, etc.)
  3. Path-based routing rules
  4. SSL and compression settings

Example template usage in code:

await caddyService.applyTemplate(proxy, template);
// Applies all template headers and middleware
// Updates Caddy configuration automatically

🛠 Technology Stack

Backend

  • Runtime: Node.js 20.x LTS
  • Framework: Express.js 5
  • Database: SQLite (default) or PostgreSQL via Sequelize ORM
  • Authentication: JWT + httpOnly cookies, bcrypt, speakeasy (TOTP)
  • API Documentation: swagger-jsdoc + swagger-ui-express at /api/docs
  • Email: nodemailer (password reset, alert notifications)
  • Testing: Jest 30 + Supertest

Frontend

  • Framework: React 19
  • Build Tool: Vite 6
  • Styling: TailwindCSS 4
  • Routing: React Router v7
  • Notifications: react-hot-toast
  • Charts: Chart.js
  • Testing: Vitest + React Testing Library

Infrastructure

  • Container: Docker & Docker Compose
  • Reverse Proxy: Caddy 2.x (custom build with caddy-dns/cloudflare)
  • Web Server: NGINX (serves SPA + proxies /api to backend)
  • CI/CD: GitHub Actions (Node 20, unit tests, build artifacts, coverage upload)

🚀 Quick Start

CaddyManager offers three deployment configurations to suit different needs:

Deployment Options Overview

Configuration Database Use Case Ports Command
Default (docker-compose.yml) SQLite Small deployments, testing Caddy: 80/443, Backend: 3000, Frontend: 8080 docker-compose up -d
Development (docker-compose.dev.yaml) SQLite Development, debugging Caddy: 80/443/2019, Backend: 3000, Frontend: 8080 docker-compose -f docker-compose.dev.yaml up -d
Production (docker-compose.prod.yml) PostgreSQL Production, high-traffic Caddy: 80/443, Backend: 3000, Frontend: 8080 docker-compose --env-file .env.prod -f docker-compose.prod.yml up -d

1. Default Deployment (SQLite - Recommended for Small Scale)

Best for: Personal use, small teams, testing, low-traffic sites

Features:

  • SQLite database (no separate DB container needed)
  • Simple setup with minimal configuration
  • All data stored in Docker volumes
  • Caddy handles SSL/TLS on ports 80/443
  • Frontend accessible on port 8080
  • Backend API on port 3000

Quick Start:

  1. Clone the repository:
git clone https://github.com/rhad00/CaddyManager.git
cd CaddyManager
  1. (Optional) Set JWT secret:
export JWT_SECRET=your_secure_random_string_here
  1. Start the application:
docker-compose up -d
  1. Access the application:

  2. Login with default credentials:

    • Email: admin@caddymanager.local
    • Password: changeme123
    • ⚠️ IMPORTANT: Change the default password immediately after first login!

Customizing Admin Credentials:

To set custom admin credentials on first startup, set these environment variables:

export ADMIN_EMAIL=your-email@example.com
export ADMIN_PASSWORD=your-secure-password
docker-compose up -d

Or add them to your docker-compose.yml:

backend:
  environment:
    - ADMIN_EMAIL=your-email@example.com
    - ADMIN_PASSWORD=your-secure-password

2. Development Deployment (SQLite)

Best for: Active development, debugging, testing new features

Features:

  • SQLite database for simplicity
  • Source code mounted for live reload
  • Caddy Admin API exposed on port 2019
  • Debug logging enabled
  • All ports exposed for direct access

Quick Start:

  1. Clone and navigate:
git clone https://github.com/rhad00/CaddyManager.git
cd CaddyManager
  1. Start development environment:
docker-compose -f docker-compose.dev.yaml up -d
  1. Access the application:

  2. Login with default credentials:

    • Email: admin@caddymanager.local
    • Password: changeme123
    • ⚠️ IMPORTANT: Change the default password immediately after first login!

3. Production Deployment (PostgreSQL - Recommended for Scale)

Best for: Production environments, high-traffic sites, enterprise deployments

Features:

  • PostgreSQL database for reliability and performance
  • Optimized for production workloads
  • Environment-based configuration
  • Secure defaults
  • Caddy handles SSL/TLS on ports 80/443

Quick Start:

  1. Clone the repository:
git clone https://github.com/rhad00/CaddyManager.git
cd CaddyManager
  1. Create production environment file:
cp .env.prod.example .env.prod
  1. Edit .env.prod with your production values:
# Required: Database password
DB_PASSWORD=your_secure_production_password

# Required: JWT secret (use a long random string)
JWT_SECRET=your_production_jwt_secret_change_this_in_production_12345678901234567890

# Optional: Customize these if needed
DB_NAME=caddymanager
DB_USER=caddyuser
JWT_EXPIRES_IN=24h
LOG_LEVEL=info
  1. Start the application:
docker-compose --env-file .env.prod -f docker-compose.prod.yml up -d
  1. Access the application:

  2. Login with default credentials:

    • Email: admin@caddymanager.local
    • Password: changeme123
    • ⚠️ IMPORTANT: Change the default password immediately after first login!

Port Reference

All deployment configurations use consistent port mappings:

  • 80/443: Caddy reverse proxy (handles your proxied services)
  • 3000: Backend API (CaddyManager API)
  • 8080: Frontend UI (CaddyManager web interface)
  • 2019: Caddy Admin API (development only, internal use)
  • 5432: PostgreSQL (production only, not exposed by default)

📋 Environment Variables Reference

Backend Environment Variables

All backend environment variables can be set in .env files or passed directly to Docker.

Database Configuration

Variable Default Description Required
DB_TYPE sqlite Database type: sqlite or postgres No
SQLITE_PATH ./database.sqlite Path to SQLite database file No (SQLite only)
DB_HOST localhost PostgreSQL host Yes (PostgreSQL)
DB_PORT 5432 PostgreSQL port No
DB_NAME caddymanager Database name Yes (PostgreSQL)
DB_USER caddyuser Database username Yes (PostgreSQL)
DB_PASSWORD - Database password Yes (PostgreSQL)
DB_SSL false Enable SSL for database connection No
DB_URL - Full database connection URL (overrides individual settings) No

Application Configuration

Variable Default Description Required
NODE_ENV development Environment: development, production, or test No
PORT 3000 Backend server port No
LOG_LEVEL info Logging level: error, warn, info, debug No

Authentication Configuration

Variable Default Description Required
JWT_SECRET - Secret key for JWT token signing (use long random string) Yes
JWT_EXPIRES_IN 24h JWT token expiration time (e.g., 24h, 7d, 30m) No
ADMIN_EMAIL admin@caddymanager.local Initial admin user email (created on first startup if no users exist) No
ADMIN_PASSWORD changeme123 Initial admin user password (created on first startup if no users exist) No
APP_NAME CaddyManager Application name shown in TOTP QR codes No

Caddy Configuration

Variable Default Description Required
CADDY_API_URL http://caddy:2019 Caddy Admin API endpoint No
CADDY_ACCESS_LOG /app/logs/access.log Path to Caddy structured JSON access log file No
CF_API_TOKEN - Cloudflare API Token for DNS-01 challenge support No

Docker Auto-Discovery Configuration

Variable Default Description Required
ENABLE_DOCKER_DISCOVERY false Enable automatic Docker container discovery and proxy creation No
ENABLE_K8S_DISCOVERY false Enable Kubernetes service discovery (not yet implemented) No
DOCKER_LABEL_PREFIX caddymanager Prefix for Docker labels used in discovery No
AUTO_REMOVE_STOPPED false Automatically remove proxies when containers stop No
DOCKER_POLL_INTERVAL 30000 Reconciliation interval in milliseconds No

Backup Configuration

Variable Default Description Required
BACKUP_ENCRYPTION_KEY - 64-char hex key (32 bytes) for AES-256-GCM backup encryption. Generate with openssl rand -hex 32. If not set, backups are stored unencrypted. No
BACKUP_RETENTION_DAYS 30 Number of days to keep auto-generated backups. Older auto-backups are deleted after each scheduled backup run. No

Git Integration & GitOps Configuration

Variable Default Description Required
GIT_REPO_DIR ./git-repos Directory where Git repositories are cloned No
GIT_SECRET_KEY - 32-byte hex key for encrypting Git access tokens (generate with openssl rand -hex 32) Yes (Production)

Important: Without GIT_SECRET_KEY, access tokens won't persist across restarts. Generate a secure key:

openssl rand -hex 32

Frontend Environment Variables

Variable Default Description Required
VITE_API_URL /api Backend API URL (use /api for Docker deployments as NGINX proxies to backend, http://localhost:3000/api for local dev without Docker) No

Example Configurations

Minimal SQLite Setup (Default/Dev)

JWT_SECRET=your_random_secret_key_here

Full PostgreSQL Setup (Production)

# Database
DB_PASSWORD=secure_password_here
DB_NAME=caddymanager
DB_USER=caddyuser

# Authentication
JWT_SECRET=your_production_jwt_secret_change_this_12345678901234567890
JWT_EXPIRES_IN=24h

# Application
NODE_ENV=production
LOG_LEVEL=info

Manual Installation

For development without Docker:

  1. Clone the repository:
git clone https://github.com/rhad00/CaddyManager.git
cd CaddyManager
  1. Install dependencies:
# Backend
cd backend
npm install

# Frontend
cd ../frontend
npm install
  1. Configure environment:
cp backend/.env.example backend/.env
cp frontend/.env.example frontend/.env
  1. Edit backend/.env with required values:
# Minimal configuration for local development
NODE_ENV=development
PORT=3000
DB_TYPE=sqlite
SQLITE_PATH=./database.sqlite
JWT_SECRET=dev_secret_key_change_in_production
CADDY_API_URL=http://localhost:2019  # Use localhost for manual installation
  1. Edit frontend/.env with required values:
# Frontend configuration for local development
VITE_API_URL=http://localhost:3000/api  # Use full URL for manual installation (no NGINX proxy)
  1. Start development servers:
# Backend (in backend directory)
npm run dev

# Frontend (in frontend directory, separate terminal)
npm run dev

Note:

  • Docker deployments: Frontend uses VITE_API_URL=/api (NGINX proxies /api to backend container). Backend uses CADDY_API_URL=http://caddy:2019 (container name).
  • Manual/local development: Frontend uses VITE_API_URL=http://localhost:3000/api (direct connection). Backend uses CADDY_API_URL=http://localhost:2019 (localhost).

📝 Documentation

Cloudflare DNS Challenge (Optional)

You can enable Cloudflare DNS-01 challenge support so Caddy can obtain/renew certificates using Cloudflare's DNS API. When enabled, the frontend UI will show a Cloudflare DNS option in the SSL type dropdown for proxies.

Important: API Token vs API Key

⚠️ CRITICAL: Caddy's Cloudflare DNS provider only supports API Tokens, not the legacy Global API Keys.

Authentication Methods Comparison:

Method Caddy Support Traefik Support Security Recommended
API Token (Modern) ✅ Yes ✅ Yes ✅ Scoped permissions Use This
Global API Key (Legacy) ❌ No ✅ Yes ⚠️ Full account access ❌ Don't Use

Why This Matters:

  • If you're migrating from Traefik or another reverse proxy that uses CF_API_KEY + CF_API_EMAIL, you must generate a new API Token
  • Using a Global API Key will result in the error: Invalid format for Authorization header
  • Caddy uses the libdns/cloudflare library which explicitly does not support legacy API keys

How to Enable Cloudflare DNS Challenge

Step 1: Generate a Cloudflare API Token

  1. Go to https://dash.cloudflare.com/profile/api-tokens
  2. Click "Create Token"
  3. Use the "Edit zone DNS" template
  4. Configure permissions:
    • Zone - Zone - Read (for all zones)
    • Zone - DNS - Edit (for all zones OR specific zones you want to manage)
  5. Click "Continue to summary""Create Token"
  6. COPY THE TOKEN IMMEDIATELY (40 characters, shown only once)

Step 2: Set the Environment Variable

Add the API token to your .env file or docker-compose configuration:

# .env file
CF_API_TOKEN=your_40_character_api_token_here

Step 3: Restart Services

docker-compose restart caddy backend

Verification

After setting up the API token:

  1. The frontend UI will automatically show the Cloudflare DNS option in the SSL type dropdown
  2. When creating/editing a proxy, select Cloudflare DNS as the SSL type
  3. Caddy will use DNS-01 challenge to obtain certificates automatically
  4. Monitor logs for successful certificate acquisition:
    docker-compose logs -f caddy

Troubleshooting

Error: Invalid format for Authorization header

  • Cause: You're using a Global API Key instead of an API Token
  • Solution: Generate a new API Token following the steps above

Error: timed out waiting for record to fully propagate

  • Cause: DNS resolver caching or network issues
  • Solution: Add custom DNS resolvers in your Caddy configuration (e.g., 1.1.1.1)

Error: expected 1 zone, got 0

  • Cause: Domain not publicly resolvable or incorrect token permissions
  • Solution: Verify domain is publicly accessible and token has correct zone permissions

Notes

  • The backend exposes a GET /api/features endpoint to detect Cloudflare support availability
  • If CF_API_TOKEN is present in the environment, the frontend will surface the Cloudflare option automatically
  • Treat ssl_type: cloudflare the same as acme for certificate issuance
  • The actual DNS challenge handling is performed by Caddy using the token you provide
  • API Tokens are more secure than Global API Keys as they have scoped permissions and can be revoked independently

Docker Auto-Discovery (NEW!)

Automatically create reverse proxy configurations for your Docker containers using simple labels. Perfect for development environments and dynamic deployments!

How It Works

When Docker Auto-Discovery is enabled, CaddyManager monitors Docker events and automatically:

  1. Detects containers with caddymanager.enable=true label
  2. Creates a disabled proxy configuration from container labels
  3. Notifies the admin via the Discovery page — no traffic is proxied yet
  4. Optionally removes proxies when containers stop
  5. Applies service templates automatically if specified

Important: Discovered proxies are always created in a disabled state. An administrator must review them in the UI and explicitly enable each one. This prevents unexpected exposure of services to the internet.

Quick Start

Step 1: Enable Docker Discovery

Add to your .env file or docker-compose configuration:

ENABLE_DOCKER_DISCOVERY=true

Restart the backend:

docker-compose restart backend

Step 2: Label Your Containers

Add labels to any container you want CaddyManager to discover:

docker run -d \
  --name my-app \
  --label caddymanager.enable=true \
  --label caddymanager.domain=myapp.example.com \
  --label caddymanager.port=3000 \
  --label caddymanager.ssl=none \
  --network caddy_net \
  my-awesome-app:latest

Step 3: Enable the proxy in CaddyManager UI

Go to Discovery in the sidebar. You will see the container listed with status disabled. Review the settings (especially the domain) and click Enable to activate it. Only then will Caddy start routing traffic.

Available Labels

Label Required Default Description
caddymanager.enable Yes - Must be true to enable discovery
caddymanager.domain Yes - Domain for the proxy (e.g., api.example.com)
caddymanager.port No 80 Container port to proxy to
caddymanager.ssl No acme SSL type: acme, cloudflare, or none
caddymanager.template No - Template to apply (e.g., authelia, keycloak)
caddymanager.security_headers No false Enable security headers
caddymanager.compression No true Enable gzip/zstd compression
caddymanager.websocket No false Enable WebSocket support

Docker Compose Integration

For a typical app with a public-facing backend and frontend (DB excluded):

services:
  db:
    image: postgres:16
    # No caddymanager labels — DB should never be exposed publicly
    networks:
      - internal

  backend:
    image: myapp-backend:latest
    labels:
      caddymanager.enable: "true"
      caddymanager.domain: "api.example.com"
      caddymanager.port: "3000"
      caddymanager.ssl: "cloudflare"
    networks:
      - internal
      - caddy_net  # Must be on caddy_net so Caddy can reach it

  frontend:
    image: myapp-frontend:latest
    labels:
      caddymanager.enable: "true"
      caddymanager.domain: "app.example.com"
      caddymanager.port: "80"
      caddymanager.ssl: "cloudflare"
    networks:
      - caddy_net  # Must be on caddy_net so Caddy can reach it

networks:
  internal:    # DB ↔ backend communication, not reachable by Caddy
  caddy_net:
    external: true  # Shared with the CaddyManager stack

CaddyManager will detect backend and frontend, create disabled proxies for both, and leave db alone. The admin then enables each proxy from the Discovery page.

Advanced Configuration

Environment Variables:

  • ENABLE_DOCKER_DISCOVERY (default: false) - Master switch
  • DOCKER_LABEL_PREFIX (default: caddymanager) - Customize label prefix
  • AUTO_REMOVE_STOPPED (default: false) - Auto-delete proxies when container stops
  • DOCKER_POLL_INTERVAL (default: 30000) - Reconciliation interval in milliseconds

API Endpoints:

  • GET /api/discovery - List all discovered services
  • GET /api/discovery/status - Check discovery service status
  • POST /api/discovery/:id/sync - Manually resync a service
  • POST /api/discovery/:id/disable - Disable auto-management
  • DELETE /api/discovery/:id - Remove discovered service

Requirements

  • Docker socket must be mounted to backend container (already configured in docker-compose.yml)
  • Containers must be on the same Docker network as Caddy (typically caddy_net)
  • Backend service needs read access to /var/run/docker.sock

Troubleshooting

Containers not being discovered:

  1. Check Docker discovery is enabled: docker-compose logs backend | grep DockerDiscovery
  2. Verify labels are correct: docker inspect <container> | grep caddymanager
  3. Ensure container is on caddy_net network
  4. Manually trigger scan: curl -X POST http://localhost:3000/api/discovery/scan -H "Authorization: Bearer <token>"

Proxy created but not accessible:

  1. Verify upstream URL: Check discovered service in database
  2. Test container connectivity: docker exec caddymanager-backend curl http://container-name:port
  3. Check Caddy logs: docker-compose logs caddy
  4. Verify DNS resolution for domain

Docker socket permission issues:

  1. Check socket is mounted: docker exec caddymanager-backend ls -la /var/run/docker.sock
  2. Verify permissions: Socket should be readable by backend container
  3. On SELinux systems, may need :z flag: /var/run/docker.sock:/var/run/docker.sock:ro,z

Examples

Development Environment:

# Auto-proxy all your dev services
docker run -d \
  --label caddymanager.enable=true \
  --label caddymanager.domain=frontend.dev \
  --label caddymanager.port=3000 \
  --label caddymanager.ssl=none \
  --network caddy_net \
  my-frontend-dev

docker run -d \
  --label caddymanager.enable=true \
  --label caddymanager.domain=api.dev \
  --label caddymanager.port=8080 \
  --label caddymanager.ssl=none \
  --network caddy_net \
  my-api-dev

Production with Template:

docker run -d \
  --label caddymanager.enable=true \
  --label caddymanager.domain=auth.example.com \
  --label caddymanager.template=authelia \
  --label caddymanager.ssl=cloudflare \
  --network caddy_net \
  authelia/authelia:latest

Git Integration & GitOps (NEW!)

Version control your Caddy configuration with Git! CaddyManager provides full Git integration for automatic backups, audit trails, and GitOps workflows.

How It Works

CaddyManager can operate in two modes:

  1. Auto-Commit Mode: Automatically commit configuration changes to Git whenever you create, update, or delete a proxy
  2. GitOps Mode: Pull configuration from Git at regular intervals and auto-apply to CaddyManager (Git as source of truth)

Both modes support full encryption, audit trails, and rollback capabilities.

Quick Start

Step 1: Set Up Git Secret Key

Generate an encryption key for securing Git access tokens:

# Generate a secure 32-byte key
echo "GIT_SECRET_KEY=$(openssl rand -hex 32)" >> .env

# Restart backend to apply
docker-compose restart backend

Step 2: Connect a Git Repository

Create a repository on GitHub, GitLab, Gitea, or Bitbucket, then connect it via the API:

curl -X POST http://localhost:3000/api/git/repositories \
  -H "Authorization: Bearer <your-jwt-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Production Config Backup",
    "provider": "github",
    "repository_url": "https://github.com/youruser/caddy-config",
    "branch": "main",
    "access_token": "ghp_xxxxxxxxxxxxx",
    "auto_commit": true,
    "auto_sync": false,
    "commit_message_template": "CaddyManager: {{changes}}"
  }'

Step 3: Watch It Work!

Now whenever you create, update, or delete a proxy, CaddyManager will automatically:

  1. Export configuration to JSON and YAML
  2. Commit changes to your Git repository
  3. Record detailed audit trail with diffs
  4. Push to remote repository

Check your Git repository - you'll see commits with detailed change descriptions!

Supported Git Providers

Provider Authentication Token Type
GitHub Personal Access Token Classic or Fine-grained PAT with repo scope
GitLab Personal Access Token Personal or Project Access Token with api scope
Gitea Application Token Application token with repository write access
Bitbucket Repository Access Token Repository access token with write permission

Operating Modes

1. Auto-Commit Mode (Recommended for Backup)

Automatically backup configuration changes to Git:

{
  "auto_commit": true,
  "auto_sync": false
}
  • Every proxy change is committed to Git
  • Complete audit trail with who, what, when
  • Easy rollback to any previous state
  • Git as backup and version history

2. GitOps Mode (Git as Source of Truth)

Let Git control your Caddy configuration:

{
  "auto_commit": false,
  "auto_sync": true,
  "sync_interval": 300
}
  • CaddyManager pulls from Git every 5 minutes (300 seconds)
  • Replaces ALL proxies with configuration from Git
  • Perfect for declarative infrastructure
  • Manage config via Git PRs and branches

3. Bidirectional Mode (Advanced)

Sync in both directions (use with caution):

{
  "auto_commit": true,
  "auto_sync": true,
  "sync_interval": 600
}

⚠️ Warning: Can cause conflicts if Git is modified externally

4. Manual Mode

Full control - trigger exports manually:

{
  "auto_commit": false,
  "auto_sync": false
}

Then manually export when needed:

curl -X POST http://localhost:3000/api/git/repositories/<repo-id>/export \
  -H "Authorization: Bearer <token>"

API Endpoints

Repository Management:

  • GET /api/git/repositories - List all connected repositories
  • POST /api/git/repositories - Connect new Git repository
  • GET /api/git/repositories/:id - Get repository details
  • PUT /api/git/repositories/:id - Update repository settings
  • DELETE /api/git/repositories/:id - Remove repository connection

Operations:

  • POST /api/git/repositories/:id/sync - Manually sync from Git (GitOps)
  • POST /api/git/repositories/:id/export - Manually export configuration
  • POST /api/git/repositories/:id/test - Test repository connection
  • POST /api/git/repositories/:id/rollback - Rollback to specific commit

History & Audit:

  • GET /api/git/history - View commit history with filters
  • GET /api/git/repositories/:id/diff - View diff between commits

Exported Files

When CaddyManager commits to Git, it exports:

your-repo/
  └── config/
      ├── proxies.json      # Machine-readable proxy config
      ├── proxies.yaml      # Human-readable proxy config
      ├── caddy.json        # Complete Caddy server config
      └── metadata.json     # Export metadata and version

Example proxies.yaml:

- id: 550e8400-e29b-41d4-a716-446655440000
  name: API Server
  domains:
    - api.example.com
  upstream_url: http://backend:3000
  ssl_type: acme
  security_headers_enabled: true
  headers:
    - key: X-Custom-Header
      value: CustomValue
      type: request

Security Features

Token Encryption:

  • All Git access tokens encrypted with AES-256-GCM
  • Unique IV (initialization vector) per encryption
  • Authentication tag for integrity verification
  • Tokens never exposed in API responses or logs

Environment Variables:

  • GIT_SECRET_KEY - 32-byte hex key (required for production)
  • GIT_REPO_DIR - Directory for cloned repos (default: ./git-repos)

Rollback & Recovery

Rollback to Any Commit:

# View commit history
curl http://localhost:3000/api/git/history \
  -H "Authorization: Bearer <token>"

# Rollback to specific commit
curl -X POST http://localhost:3000/api/git/repositories/<repo-id>/rollback \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{"commit_sha": "abc123..."}'

Safety Features:

  • Creates backup branch before rollback
  • Original state preserved in backup-<timestamp> branch
  • Applies old configuration via GitOps sync
  • Returns to main branch after rollback

Commit Message Templates

Customize commit messages with templates:

{
  "commit_message_template": "🚀 CaddyManager: {{changes}}\n\nAutomated commit from production"
}

Template Variables:

  • {{changes}} - Auto-generated change description

Example Generated Commits:

🚀 CaddyManager: Created proxy: api.example.com
Domains: api.example.com, www.example.com

Automated commit from production
🚀 CaddyManager: Updated proxy: api.example.com
Upstream: http://old:3000 → http://new:3000

Automated commit from production

Troubleshooting

Repository connection fails:

  1. Verify access token has required permissions:
    • GitHub: repo scope for private repos
    • GitLab: api or write_repository scope
    • Gitea: Repository write access
  2. Check repository URL is HTTPS (not SSH)
  3. Test token manually: git clone https://<token>@github.com/user/repo.git

Commits not appearing in Git:

  1. Check repository settings:
    curl http://localhost:3000/api/git/repositories \
      -H "Authorization: Bearer <token>"
  2. Verify auto_commit: true and enabled: true
  3. Check backend logs: docker-compose logs backend | grep GitService
  4. Ensure branch exists on remote repository

GitOps sync not working:

  1. Verify auto_sync: true and enabled: true
  2. Check config/proxies.json exists in repository
  3. Watch logs during sync interval
  4. Manually trigger sync to test:
    curl -X POST http://localhost:3000/api/git/repositories/<id>/sync \
      -H "Authorization: Bearer <token>"

Tokens not persisting across restarts:

  1. Set GIT_SECRET_KEY environment variable
  2. Use same key across all backend instances
  3. Generate with: openssl rand -hex 32

Complete Example Workflow

Scenario: Set up automated backups to GitHub with rollback capability

# 1. Generate encryption key
echo "GIT_SECRET_KEY=$(openssl rand -hex 32)" >> .env
docker-compose restart backend

# 2. Create GitHub repository
# Visit github.com and create new repository: caddy-config-backup

# 3. Generate GitHub Personal Access Token
# Settings → Developer settings → Personal access tokens → Generate new token
# Select 'repo' scope

# 4. Connect repository to CaddyManager
curl -X POST http://localhost:3000/api/git/repositories \
  -H "Authorization: Bearer <jwt>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "GitHub Backup",
    "provider": "github",
    "repository_url": "https://github.com/youruser/caddy-config-backup",
    "branch": "main",
    "access_token": "ghp_your_token_here",
    "auto_commit": true,
    "auto_sync": false,
    "commit_message_template": "CaddyManager: {{changes}}"
  }'

# 5. Create a test proxy (will auto-commit)
curl -X POST http://localhost:3000/api/proxies \
  -H "Authorization: Bearer <jwt>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Test Service",
    "domains": ["test.example.com"],
    "upstream_url": "http://backend:3000",
    "ssl_type": "acme"
  }'

# 6. Verify commit in GitHub
# Check your repository - new commit should appear!

# 7. View audit history
curl http://localhost:3000/api/git/history \
  -H "Authorization: Bearer <jwt>"

# 8. Rollback if needed
curl -X POST http://localhost:3000/api/git/repositories/<repo-id>/rollback \
  -H "Authorization: Bearer <jwt>" \
  -d '{"commit_sha": "abc123..."}'

Result: Every configuration change is now automatically backed up to GitHub with complete audit trail and one-click rollback!


Load Balancing & Health Checks

Distribute traffic across multiple backends with automatic failover.

Configure Load Balancing

When creating or editing a proxy, add multiple upstream URLs and choose a balancing strategy:

Strategy Description
round_robin Distribute requests evenly across all upstreams (default)
least_conn Route to the upstream with fewest active connections
ip_hash Sticky sessions — same client IP always hits same upstream
random Random upstream selection
weighted Weighted distribution (specify weight per upstream)

Configure Health Checks

Health checks run in the background and automatically remove unhealthy upstreams from rotation:

Field Description Default
Check path HTTP path to probe (e.g., /health) /
Interval How often to check (seconds) 30s
Timeout Max time to wait for response 5s
Max fails Consecutive failures before marking down 3
Expect status Expected HTTP status code 200

Both features are configured in the ProxyForm UI under the "Load Balancing" and "Health Checks" sections, or via the API:

curl -X POST http://localhost:3000/api/proxies \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My Scaled Service",
    "domains": ["api.example.com"],
    "ssl_type": "acme",
    "load_balancing": {
      "strategy": "least_conn",
      "upstreams": [
        { "url": "http://backend1:3000", "weight": 1 },
        { "url": "http://backend2:3000", "weight": 1 }
      ]
    },
    "health_checks": {
      "path": "/health",
      "interval": "30s",
      "timeout": "5s",
      "max_fails": 3
    }
  }'

Access Log Viewer

View and search Caddy's structured access logs directly from the Dashboard.

Setup

Caddy access logging is configured automatically when CaddyManager initializes. Logs are written to CADDY_ACCESS_LOG (default: /app/logs/access.log) in JSON format.

Features

  • Live Tail: Click "Live" in the Access Logs tab to stream new log entries in real time via SSE
  • Historical Search: Filter by domain, HTTP status code, client IP, or time range
  • Stats: View total request count, error rate, and top domains via GET /api/logs/stats

API

# Query historical logs
GET /api/logs?domain=api.example.com&status=500&limit=100

# Live tail (SSE stream)
GET /api/logs/stream?token=<jwt>

# Log statistics
GET /api/logs/stats

Alerting & Notifications

Get proactively notified before issues affect your users.

Step 1: Create a Notification Channel

Go to Dashboard → Alerts → Channels and add a channel:

Type Required Config
Email SMTP host, port, username, password, from/to addresses
Slack Incoming webhook URL
Discord Webhook URL
Webhook URL, optional secret header

Test any channel immediately with the "Test" button.

Step 2: Create Alert Rules

Go to Dashboard → Alerts → Rules and define conditions:

Condition Description Threshold
cert_expiry Certificate expires within N days Days remaining
upstream_down Upstream health check fails Failure count
error_rate HTTP 5xx response rate exceeds threshold Percentage (0-100)
no_traffic No requests received for N minutes Minutes of silence

Each rule can target a specific proxy or all proxies, and you can assign multiple notification channels.

Evaluation Schedule

Alert rules are evaluated automatically every 15 minutes. You can also trigger a manual check from the Alerts tab with the "Run Checks Now" button.


Two-Factor Authentication

Protect admin accounts with TOTP-based 2FA (compatible with Google Authenticator, Authy, 1Password, and any RFC 6238 app).

Setting Up 2FA (per user)

  1. Log in and navigate to Dashboard → My Account
  2. Click "Set Up Two-Factor Authentication"
  3. Scan the QR code with your authenticator app (or enter the manual key)
  4. Enter the 6-digit code from your app to confirm and activate
  5. Save your backup codes — shown once, each is single-use for account recovery

Login Flow

When 2FA is enabled, login becomes a two-step process:

Step 1: Enter email + password → server returns a short-lived challenge token
Step 2: Enter 6-digit TOTP code (or 8-character backup code) → receive full session token

Disabling 2FA

Go to Dashboard → My Account, click "Disable 2FA", and confirm with a current TOTP code.


API Key Authentication

Create long-lived keys for scripts, CI/CD pipelines, and automation tools.

Creating an API Key

Go to Dashboard → My Account → API Keys and click "Create New Key":

  • Name: A label for tracking (e.g., "GitHub Actions deploy")
  • Permissions: read (GET only), write (GET + POST + PUT), admin (full access)
  • Expiry: Optional expiry date; leave blank for non-expiring keys

The raw key is shown only once — copy it immediately.

Using an API Key

Pass the key in the X-API-Key header on any API request:

curl http://localhost:3000/api/proxies \
  -H "X-API-Key: cm_live_xxxxxxxxxxxxxxxx"

The key is verified by SHA-256 hash (the plaintext key is never stored). Each request updates last_used_at for auditing.


Backup Encryption & Retention

Encrypted Backups

Set BACKUP_ENCRYPTION_KEY to enable AES-256-GCM encryption for all backup files:

# Generate a secure key
BACKUP_ENCRYPTION_KEY=$(openssl rand -hex 32)

Encrypted backups use the .json.enc extension. Unencrypted backups (.json) are still readable if the key is not set — the system auto-detects the format on restore.

Retention Policy

Set BACKUP_RETENTION_DAYS (default: 30) to automatically delete old auto-generated backups:

BACKUP_RETENTION_DAYS=14  # Keep only the last 14 days of auto-backups

Retention is enforced after every scheduled backup run. Manual backups are not affected.

Environment Variables

BACKUP_ENCRYPTION_KEY=<64-char hex string>  # openssl rand -hex 32
BACKUP_RETENTION_DAYS=30                    # days to keep auto-backups

OpenAPI / Swagger Documentation

Interactive API documentation is available at:

  • Swagger UI: http://localhost:3000/api/docs
  • Raw OpenAPI JSON: http://localhost:3000/api/docs/openapi.json

The spec covers all endpoints with request/response schemas, authentication methods (Bearer JWT and X-API-Key), and example payloads. The Swagger UI is accessible without authentication.


🧪 Testing

CaddyManager includes comprehensive integration tests for all features:

Available Test Scripts

Core Features:

./test.sh all          # Run all tests
./test.sh auth         # Test authentication
./test.sh proxy        # Test proxy management
./test.sh template     # Test template functionality
./test.sh backup       # Test backup and restore
./test.sh caddy        # Test Caddy integration

Docker Auto-Discovery:

./discovery_test.sh    # Test Docker discovery features

Tests include:

  • Discovery service status checks
  • Manual container scanning
  • Service management operations (sync, enable/disable)
  • Automatic container detection
  • Label-based configuration

Git Integration & GitOps:

./git_integration_test.sh    # Test Git integration features

Tests include:

  • Repository connection management
  • Configuration export to Git
  • Commit history tracking
  • Repository connection testing

Full Integration Tests:

./integration_test.sh           # Complete integration test suite
./persistence_validation.sh     # Test data persistence
./certificate_validation.sh     # Validate TLS certificates
./metrics_test.sh               # Test metrics collection

Running Tests with Docker

Make sure the backend is running before executing tests:

docker-compose up -d
./test.sh all

For Git integration tests with real repositories:

export TEST_GIT_URL="https://github.com/user/test-repo"
export TEST_GIT_TOKEN="ghp_xxxxxxxxxxxxx"
export GIT_SECRET_KEY=$(openssl rand -hex 32)
./git_integration_test.sh

📚 Documentation

  • CLAUDE.md — Architecture deep-dive, service descriptions, and development guide
  • CONTRIBUTING.md — Contributing guidelines
  • FEATURE_IMPLEMENTATION.md — Detailed implementation notes
  • Swagger / OpenAPI: http://localhost:3000/api/docs (live instance required)

🗺 Roadmap

Completed ✅

  • Load balancing with multiple upstreams and health checks
  • Access log viewer (live tail + historical search)
  • Alerting & notifications (email, Slack, Discord, webhook)
  • Two-factor authentication (TOTP)
  • API key authentication
  • Custom template creator UI
  • Backup encryption (AES-256-GCM) + retention policy
  • OpenAPI / Swagger documentation
  • CI/CD pipeline (GitHub Actions)
  • Docker Auto-Discovery
  • Git Integration & GitOps

Planned

  • S3/cloud backup storage
  • LDAP / Active Directory authentication
  • Kubernetes service discovery
  • Advanced request routing (header/query/method conditions)
  • Multi-node clustering & high availability
  • WAF integration (CrowdSec)
  • Plugin/extension system
  • OAuth2 / SAML SSO
  • Prometheus metrics export endpoint
  • Dark/light theme toggle

👥 Contributing

We welcome contributions! Please see our Contributing Guidelines for details.

  1. Fork the repository
  2. Create your feature branch:
    git checkout -b feature/amazing-feature
  3. Commit your changes:
    git commit -m 'Add amazing feature'
  4. Push to the branch:
    git push origin feature/amazing-feature
  5. Open a Pull Request

🐛 Bug Reports & Feature Requests

Please use the GitHub issue tracker to report bugs or suggest features.

When reporting bugs, please include:

  • Detailed description of the issue
  • Steps to reproduce
  • Expected vs actual behavior
  • CaddyManager version
  • Environment details (OS, Docker version if applicable)
  • Relevant logs or screenshots

📄 License

This project is licensed under the Apache License 2.0 - see the LICENSE file for details.

🙏 Acknowledgments


Made with ❤️ by the CaddyManager Team

About

an open-source reverse proxy manager built on Caddy Server with a web-based UI and REST API backend.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages