Networking
This document explains how to configure DNS and Caddy reverse proxy in Composia.
DNS Configuration
Composia supports automatic DNS record management. Currently only Cloudflare is supported.
Controller Configuration
controller:
dns:
cloudflare:
api_token_file: "/app/configs/cloudflare-token.txt"Create the API Token file:
echo "your-cloudflare-api-token" > configs/cloudflare-token.txtCloudflare Token Permissions Required:
- Zone:Read
- DNS:Edit
Service DNS Configuration
Configure in the service's composia-meta.yaml:
name: my-app
nodes:
- main
network:
dns:
provider: cloudflare
hostname: app.example.com
record_type: A # A, AAAA, or CNAME
proxied: true # Enable Cloudflare proxy
ttl: 120 # TTL in seconds
# value: "1.2.3.4" # Optional, manually specify record valueAutomatic IP Derivation
If value is not specified, Composia attempts to automatically derive it from node configuration:
controller:
nodes:
- id: "main"
public_ipv4: "203.0.113.10" # Used for A records
public_ipv6: "2001:db8::1" # Used for AAAA recordsNote: Automatic derivation is only suitable for single-node services. For multi-node services, explicitly provide value.
Trigger DNS Update
DNS updates are typically triggered automatically in the following cases:
- Deploying a new service instance
- Migrating a service to a new node
- Manually executing
dns_updatetask
Manual trigger:
curl -X POST http://localhost:7001/api/v1/services/my-app/dns-update \
-H "Authorization: Bearer YOUR_TOKEN"DNS Configuration Examples
Basic A Record:
network:
dns:
provider: cloudflare
hostname: api.example.com
record_type: AEnable Cloudflare Proxy:
network:
dns:
provider: cloudflare
hostname: app.example.com
record_type: A
proxied: true
ttl: 1 # TTL automatically managed in automatic modeIPv6 Support:
network:
dns:
provider: cloudflare
hostname: app.example.com
record_type: AAAAMultiple Domains:
Configure separate services for each domain or use wildcards.
Caddy Reverse Proxy
Composia supports automatic generation and synchronization of Caddy configuration fragments.
Architecture
Service (composia-meta.yaml)
│ network.caddy.enabled: true
▼
Controller (generates config fragment)
│
▼
Agent (distributes to nodes)
│ writes to generated_dir
▼
Caddy (loads config and reloads)1. Deploy Caddy Infrastructure Service
Create a Caddy infrastructure service:
# infra-caddy/composia-meta.yaml
name: infra-caddy
nodes:
- main
enabled: true
infra:
caddy:
compose_service: caddy # Compose service name
config_dir: /etc/caddy # Caddy configuration directory# infra-caddy/docker-compose.yaml
services:
caddy:
image: caddy:2-alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
- /srv/caddy/generated:/etc/caddy/conf.d # Generated config directory
command: caddy run --config /etc/caddy/Caddyfile --adapter caddyfile
volumes:
caddy_data:
caddy_config:# infra-caddy/Caddyfile
# Import generated configurations
import /etc/caddy/conf.d/*.conf
# Optional: default response
:80 {
respond "Caddy is running"
}2. Configure Agent
agent:
controller_addr: "http://controller:7001"
node_id: "main"
token: "main-agent-token"
caddy:
generated_dir: "/srv/caddy/generated" # Must match Caddy container mount path3. Configure Business Service
Add configuration to services that need Caddy proxy:
# my-app/composia-meta.yaml
name: my-app
nodes:
- main
network:
caddy:
enabled: true
source: ./Caddyfile.fragmentCreate the Caddy configuration fragment:
# my-app/Caddyfile.fragment
app.example.com {
reverse_proxy localhost:8080
# Security headers
header {
X-Frame-Options "SAMEORIGIN"
X-Content-Type-Options "nosniff"
X-XSS-Protection "1; mode=block"
Referrer-Policy "strict-origin-when-cross-origin"
}
# Gzip compression
encode gzip
# Logging
log {
output file /var/log/caddy/app.log
format json
}
# TLS (automatic Let's Encrypt)
tls {
protocols tls1.2 tls1.3
}
}4. Automated Behavior
Caddy configuration is automatically synchronized in the following cases:
| Operation | Automated Behavior |
|---|---|
deploy | Triggers caddy_sync + caddy_reload after success |
update | Triggers caddy_sync + caddy_reload after success |
stop | Removes generated fragment and triggers caddy_reload |
migrate | Removes config from source node, adds to target node |
Caddy Configuration Fragment Templates
Basic Reverse Proxy:
app.example.com {
reverse_proxy localhost:3000
}With Load Balancing (Multiple Instances):
app.example.com {
reverse_proxy localhost:3000 localhost:3001 localhost:3002 {
lb_policy round_robin
health_uri /health
health_interval 10s
}
}With Basic Authentication:
app.example.com {
basicauth {
admin $2a$14$Zkx19XLiW6VYouLHR5NmfOFU0z2GTNmpkT/5qqR7hx4IjWJPDhjvG
}
reverse_proxy localhost:3000
}WebSocket Support:
app.example.com {
reverse_proxy localhost:3000 {
header_up Upgrade {>Upgrade}
header_up Connection {>Connection}
}
}Rate Limiting:
app.example.com {
rate_limit {
zone static_example {
key static
events 100
window 1m
}
}
reverse_proxy localhost:3000
}Complete Example
Deploy a Complete Web Application
Directory Structure:
my-webapp/
├── composia-meta.yaml
├── docker-compose.yaml
└── Caddyfile.fragmentcomposia-meta.yaml:
name: my-webapp
nodes:
- main
network:
caddy:
enabled: true
source: ./Caddyfile.fragment
dns:
provider: cloudflare
hostname: app.example.com
record_type: A
proxied: true
data_protect:
data:
- name: uploads
backup:
strategy: files.copy
include:
- ./data/uploads
restore:
strategy: files.copy
include:
- ./data/uploads
backup:
data:
- name: uploads
provider: rusticdocker-compose.yaml:
services:
app:
image: myapp:1.0.0
ports:
- "127.0.0.1:8080:8080" # Local only, exposed via Caddy
volumes:
- ./data/uploads:/app/uploads
environment:
- NODE_ENV=production
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3Caddyfile.fragment:
app.example.com {
reverse_proxy localhost:8080
header {
X-Frame-Options "SAMEORIGIN"
X-Content-Type-Options "nosniff"
}
encode gzip
log {
output file /var/log/caddy/my-webapp.log
}
}Deployment Steps:
- Ensure Caddy infrastructure service is deployed
- Commit
my-webappdirectory to Git repository - Find
my-webappservice in Web UI - Click Deploy
- DNS configuration and Caddy sync complete automatically
- Visit
https://app.example.com
Troubleshooting
DNS Not Updated
Check:
- Is Controller configured with
dns.cloudflare? - Is Cloudflare API Token valid?
- Is domain Zone correct?
Caddy Configuration Not Applied
Check:
- Is Caddy infrastructure service running?
- Is Agent's
caddy.generated_dircorrect? - Is Caddy container correctly mounting the generated directory?
- View Caddy logs:
docker logs infra-caddy-caddy-1
HTTPS Certificate Issues
- Ensure certificate directory is persisted (
caddy_datavolume) - Check if domain DNS correctly points to server
- View Caddy logs for certificate request status
Related Documentation
- Service Definition — Complete service configuration reference
- Deployment — Service deployment flow
- Caddy Official Documentation — Caddy configuration reference