Caddy Proxy & Custom Rules
Host port 80/443 binding, edge reverse proxy architecture, and writing custom Caddy configurations
Explita Deploy runs an automated, containerized Caddy edge reverse proxy (explita-caddy) that binds directly to your host server's public HTTP (80) and HTTPS (443) ports. It handles automated TLS certificate generation (via Let's Encrypt and ZeroSSL), zero-downtime container cutovers, and instant 503 maintenance mode.
1. Why Ports 80 & 443 Are Reserved
When Explita Deploy initializes, the explita-caddy container binds directly to host ports 80 and 443:
docker run -d \
--name explita-caddy \
-p 80:80 \
-p 443:443 \
...[!WARNING] Do not install a second reverse proxy directly on the host. If you attempt to install Nginx, Apache, Traefik, or a second Caddy instance on the host OS (e.g. via
apt install nginx), the service will fail to bind withbind: address already in usebecauseexplita-caddyis actively listening on ports80and443.
Why Run One Centralized Edge Proxy?
Rather than fragmenting ports or juggling multiple web servers fighting for inbound traffic, explita-caddy acts as the single edge ingress gateway for your entire server.
You do not need another web server. You can route traffic to any service on your server—whether deployed via Explita, running as standalone Docker containers, or executing as independent host processes—by adding custom Caddy configuration files.
2. Isolated Custom Rules Directory
To prevent custom user configurations from conflicting with automated deployments, Explita provides a dedicated, isolated custom directory:
| Location | Path |
|---|---|
| Host System | /var/lib/explita/custom/caddy/ |
| Container Mount | /etc/caddy/custom/ |
How Caddy Loads Configurations
The master /etc/caddy/Caddyfile imports two separate directory trees:
{
# Global options
}
# Automated application routes managed by Explita Deploy
import /etc/caddy/apps/*.caddy
# Your custom user-defined rules and standalone domains
import /etc/caddy/custom/*.caddy/etc/caddy/apps/*.caddy: Strictly managed by Explita Deploy (application routes, domain mappings, and preview environments)./etc/caddy/custom/*.caddy: Completely owned by you. Explita will never modify, overwrite, or delete files in this directory.
3. Common Use Cases & Examples
Drop any file ending in .caddy into /var/lib/explita/custom/caddy/ (e.g. api.caddy, wordpress.caddy, redirects.caddy).
Use Case A: Proxying to a Host Service (Non-Docker)
If you have a database GUI, Go binary, Python daemon, or Node.js service running directly on the host (e.g. port 8080), use host.docker.internal:
admin.example.com {
reverse_proxy host.docker.internal:8080
}[!NOTE]
explita-caddyis started with--add-host host.docker.internal:host-gateway, enabling the container to resolve and communicate with any host-bound port.
Use Case B: Proxying to Another Docker Container
If you run standalone Docker containers (e.g., Plausible Analytics, Grafana, or a custom Docker Compose stack), attach them to the explita-net Docker network and proxy directly by container name:
analytics.example.com {
reverse_proxy plausible-container:8000
}# Connect an existing container to Explita's network
docker network connect explita-net plausible-containerUse Case C: Static Site or Documentation Hosting
You can serve static files (e.g. HTML/CSS documentation or a React SPA build) directly from disk. Static assets placed in /var/lib/explita/custom/files/ are mounted inside Caddy at /etc/caddy/files:
docs.example.com {
root * /etc/caddy/files/docs
file_server
try_files {path} /index.html
}Use Case D: Global Domain Redirects
Redirect legacy domains or enforce non-www canonical URLs with automated HTTPS:
www.example.com {
redir https://example.com{uri} permanent
}
old-brand.com {
redir https://new-brand.com{uri} permanent
}Use Case E: Basic Authentication & Custom Headers
Protect an internal route with HTTP Basic Auth and custom security headers:
internal.example.com {
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
}
basic_auth {
# Generate password hash: docker exec -i explita-caddy caddy hash-password
admin $2a$14$Zkx19XRWZxTo4O...
}
reverse_proxy host.docker.internal:9000
}4. Applying and Reloading Configuration
Caddy reloads configuration atomically with zero downtime and zero dropped connections.
1. Validate Syntax
Before applying changes, test your syntax inside the container:
docker exec -i explita-caddy caddy fmt --diff2. Reload Caddy
Apply your new rules immediately:
docker exec explita-caddy caddy reload --config /etc/caddy/Caddyfile3. Check Proxy Logs
Monitor TLS certificate provisioning or inspect incoming requests in real-time:
docker logs -f --tail 100 explita-caddy5. Summary Architecture
Host Ports :80 & :443 (Internet Traffic)
│
▼
┌──────────────────────────────────────────────┐
│ explita-caddy Container │
│ ├── /etc/caddy/Caddyfile │
│ │ │
│ ├── /etc/caddy/apps/*.caddy │
│ │ └── Managed by Explita Deploy │
│ │ (eda apps, domains, previews) │
│ │ │
│ └── /etc/caddy/custom/*.caddy │
│ └── Managed by YOU │
│ (/var/lib/explita/custom/caddy/) │
└──────────────┬───────────────────────────────┘
│
┌───────┴────────────────────────┐
▼ ▼
[Explita App Containers] [Host / Custom Services]
(e.g. app-123:3000) (host.docker.internal:8080)