Explita InfraExplita Infra
Deploy

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 with bind: address already in use because explita-caddy is actively listening on ports 80 and 443.

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:

LocationPath
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:

/var/lib/explita/custom/caddy/host-service.caddy
admin.example.com {
    reverse_proxy host.docker.internal:8080
}

[!NOTE] explita-caddy is 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:

/var/lib/explita/custom/caddy/analytics.caddy
analytics.example.com {
    reverse_proxy plausible-container:8000
}
# Connect an existing container to Explita's network
docker network connect explita-net plausible-container

Use 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:

/var/lib/explita/custom/caddy/docs.caddy
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:

/var/lib/explita/custom/caddy/redirects.caddy
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:

/var/lib/explita/custom/caddy/internal.caddy
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 --diff

2. Reload Caddy

Apply your new rules immediately:

docker exec explita-caddy caddy reload --config /etc/caddy/Caddyfile

3. Check Proxy Logs

Monitor TLS certificate provisioning or inspect incoming requests in real-time:

docker logs -f --tail 100 explita-caddy

5. 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)

On this page