| name | dokploy-cloudflare-integration |
| description | Integrate Cloudflare services with Dokploy templates: R2 storage, DNS challenge for SSL, Zero Trust Access, Workers, WAF, and Tunnel. Default to CF services for external dependencies. |
| version | 1.0.0 |
| author | Home Lab Infrastructure Team |
Dokploy Cloudflare Integration
When to Use This Skill
- When application needs S3-compatible object storage (use R2)
- When DNS challenge is needed for SSL certificates
- When Zero Trust access control is required for admin interfaces
- When CDN caching would benefit the application
- When deploying behind Cloudflare Tunnel
- When user asks about "Cloudflare integration" or "R2 storage"
When NOT to Use This Skill
- For applications without external storage needs
- For internal-only services not exposed to internet
- When user explicitly wants non-Cloudflare solutions
Prerequisites
- Cloudflare account with R2 enabled (if using storage)
- Cloudflare API token for DNS challenge (if using)
- Understanding of Cloudflare Zero Trust (if using access control)
Service Defaults
When creating Dokploy templates, DEFAULT to Cloudflare services:
| Need | Cloudflare Service | Instead Of |
|---|---|---|
| Object storage | R2 | MinIO, AWS S3 |
| SSL certificates | DNS challenge | HTTP challenge |
| CDN/Caching | Cloudflare Proxy | None |
| Access control | Zero Trust Access | Basic auth |
| DDoS protection | Cloudflare WAF | None |
| Private services | Cloudflare Tunnel | VPN |
Core Patterns
Pattern 1: Cloudflare R2 Storage
R2 is S3-compatible, so configure using S3 environment variables:
environment:
# ===========================================
# Cloudflare R2 Storage Configuration
# Get from: Cloudflare Dashboard > R2 > Manage R2 API Tokens
# Endpoint format: https://<ACCOUNT_ID>.r2.cloudflarestorage.com
# ===========================================
S3_ENDPOINT: ${S3_ENDPOINT:?Set Cloudflare R2 endpoint}
S3_REGION: ${S3_REGION:-auto}
S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID:?Set R2 access key ID}
S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY:?Set R2 secret access key}
S3_BUCKET: ${S3_BUCKET:?Set R2 bucket name}
S3_FORCE_PATH_STYLE: "false"
Template.toml Configuration:
[config.env]
# ===========================================
# Cloudflare R2 Storage
# Get from: Cloudflare Dashboard > R2 > Manage R2 API Tokens
# Endpoint format: https://<ACCOUNT_ID>.r2.cloudflarestorage.com
#
# To create R2 API Token:
# 1. Go to Cloudflare Dashboard > R2 > Overview
# 2. Click "Manage R2 API Tokens"
# 3. Create token with "Object Read & Write" permission
# 4. Copy the Access Key ID and Secret Access Key
# ===========================================
S3_ENDPOINT = ""
S3_ACCESS_KEY_ID = ""
S3_SECRET_ACCESS_KEY = ""
S3_BUCKET = ""
S3_REGION = "auto"
R2 CORS Configuration (for direct uploads):
[
{
"AllowedOrigins": ["https://your-domain.com"],
"AllowedMethods": ["GET", "PUT", "POST", "DELETE", "HEAD"],
"AllowedHeaders": ["*"],
"ExposeHeaders": ["ETag"],
"MaxAgeSeconds": 3600
}
]
Pattern 2: DNS Challenge for SSL
For wildcard certificates or when HTTP challenge isn't possible:
Traefik Static Configuration (traefik.yml):
certificatesResolvers:
cloudflare:
acme:
email: your-email@example.com
storage: /letsencrypt/acme.json
dnsChallenge:
provider: cloudflare
resolvers:
- "1.1.1.1:53"
- "1.0.0.1:53"
Environment for Traefik:
environment:
CF_API_EMAIL: ${CF_API_EMAIL:?Set Cloudflare email}
CF_DNS_API_TOKEN: ${CF_DNS_API_TOKEN:?Set Cloudflare DNS API token}
Service Labels:
labels:
- "traefik.http.routers.app.tls.certresolver=cloudflare"
- "traefik.http.routers.app.tls.domains[0].main=${BASE_DOMAIN}"
- "traefik.http.routers.app.tls.domains[0].sans=*.${BASE_DOMAIN}"
Pattern 3: Zero Trust Access (Admin Interfaces)
Protect admin interfaces with Cloudflare Access:
Option A: Cloudflare Access via Traefik Middleware
labels:
- "traefik.enable=true"
# Main app - public
- "traefik.http.routers.app.rule=Host(`${DOMAIN}`)"
- "traefik.http.routers.app.entrypoints=websecure"
- "traefik.http.routers.app.tls.certresolver=letsencrypt"
- "traefik.http.services.app.loadbalancer.server.port=8080"
# Admin - protected by Zero Trust
- "traefik.http.routers.admin.rule=Host(`admin.${DOMAIN}`)"
- "traefik.http.routers.admin.entrypoints=websecure"
- "traefik.http.routers.admin.tls.certresolver=letsencrypt"
- "traefik.http.routers.admin.middlewares=cf-access@file"
- "traefik.http.services.admin.loadbalancer.server.port=9000"
- "traefik.docker.network=dokploy-network"
Cloudflare Access Application Setup:
- Cloudflare Dashboard > Zero Trust > Access > Applications
- Create Self-hosted application
- Set Application URL:
https://admin.your-domain.com - Configure Access Policy (email domain, groups, etc.)
Pattern 4: Cloudflare Tunnel (Private Services)
Expose services without public IP:
cloudflared Container in Compose:
services:
cloudflared:
image: cloudflare/cloudflared:latest
restart: always
command: tunnel run
environment:
TUNNEL_TOKEN: ${TUNNEL_TOKEN:?Set Cloudflare Tunnel token}
networks:
- app-net # Same network as the app
app:
image: myapp:1.0.0
networks:
- app-net
# No dokploy-network needed - not exposed via Traefik
# No Traefik labels - exposed via Tunnel
Template.toml for Tunnel:
[config.env]
# ===========================================
# Cloudflare Tunnel
# Create tunnel: cloudflared tunnel create myapp
# Get token from Cloudflare Dashboard > Zero Trust > Networks > Tunnels
# ===========================================
TUNNEL_TOKEN = ""
Pattern 5: Cloudflare Workers Integration
For edge computing or API transformations:
Document in README:
## Cloudflare Workers Integration
This application supports Cloudflare Workers for edge processing.
### Use Cases
- Image optimization at the edge
- API response caching
- Request/response transformation
- A/B testing
### Setup
1. Create Worker in Cloudflare Dashboard
2. Configure Worker Route: `api.${DOMAIN}/*`
3. Set origin to your Dokploy deployment
Pattern 6: WAF Configuration
Document WAF recommendations in README:
## Cloudflare WAF Configuration
### Recommended Rules
1. **Enable Managed Rules**: OWASP Core Rule Set
2. **Rate Limiting**: 100 requests/minute per IP to `/api/*`
3. **Bot Management**: Block known bad bots
4. **Geographic Restrictions**: If applicable
### Custom Rules
- Block requests without User-Agent header
- Challenge requests from TOR exit nodes (if desired)
- Protect admin paths with additional challenges
Complete Examples
Example 1: Paaster with R2 Storage
services:
paaster:
image: wardpearce/paaster:3.1.7
restart: always
depends_on:
mongodb:
condition: service_healthy
environment:
# Domain
PAASTER_DOMAIN: ${PAASTER_DOMAIN:?Set your domain}
# Security
COOKIE_SECRET: ${COOKIE_SECRET:?Set a secure random cookie secret}
# MongoDB
MONGO_DB: ${MONGO_DB:-paasterv3}
MONGO_URL: mongodb://mongodb:27017/${MONGO_DB:-paasterv3}
# ===========================================
# Cloudflare R2 Storage
# Get from: Cloudflare Dashboard > R2 > Manage R2 API Tokens
# Endpoint format: https://<ACCOUNT_ID>.r2.cloudflarestorage.com
# ===========================================
S3_ENDPOINT: ${S3_ENDPOINT:?Set Cloudflare R2 endpoint}
S3_REGION: ${S3_REGION:-auto}
S3_ACCESS_KEY_ID: ${S3_ACCESS_KEY_ID:?Set R2 access key ID}
S3_SECRET_ACCESS_KEY: ${S3_SECRET_ACCESS_KEY:?Set R2 secret access key}
S3_BUCKET: ${S3_BUCKET:?Set R2 bucket name}
S3_FORCE_PATH_STYLE: "false"
networks:
- paaster-net
- dokploy-network
labels:
- "traefik.enable=true"
- "traefik.http.routers.paaster.rule=Host(`${PAASTER_DOMAIN}`)"
- "traefik.http.routers.paaster.entrypoints=websecure"
- "traefik.http.routers.paaster.tls.certresolver=letsencrypt"
- "traefik.http.services.paaster.loadbalancer.server.port=3000"
- "traefik.docker.network=dokploy-network"
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:3000"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
mongodb:
image: mongo:7
restart: always
volumes:
- mongodb-data:/data/db
environment:
MONGO_INITDB_DATABASE: ${MONGO_DB:-paasterv3}
networks:
- paaster-net
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
volumes:
mongodb-data:
driver: local
networks:
paaster-net:
driver: bridge
dokploy-network:
external: true
Example 2: App with Cloudflare Tunnel (Private Service)
services:
cloudflared:
image: cloudflare/cloudflared:latest
restart: always
command: tunnel run
environment:
TUNNEL_TOKEN: ${TUNNEL_TOKEN:?Set Cloudflare Tunnel token}
networks:
- app-net
depends_on:
app:
condition: service_healthy
app:
image: myapp:1.0.0
restart: always
depends_on:
postgres:
condition: service_healthy
environment:
DATABASE_URL: postgresql://user:${DB_PASS}@postgres:5432/app
networks:
- app-net
# Note: No dokploy-network or Traefik labels
# Traffic flows through Cloudflare Tunnel only
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
postgres:
image: postgres:16-alpine
restart: always
volumes:
- postgres-data:/var/lib/postgresql/data
environment:
POSTGRES_DB: app
POSTGRES_USER: user
POSTGRES_PASSWORD: ${DB_PASS:?Set database password}
networks:
- app-net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d app"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
volumes:
postgres-data:
driver: local
networks:
app-net:
driver: bridge
# Note: No dokploy-network needed for tunnel-only deployment
README Documentation Template
Include this section in template READMEs when using Cloudflare services:
## Cloudflare R2 Setup
This template uses Cloudflare R2 for object storage.
### Create R2 Bucket
1. Go to [Cloudflare Dashboard](https://dash.cloudflare.com) > R2 > Overview
2. Click "Create bucket"
3. Name your bucket (e.g., `myapp-storage`)
4. Note the bucket name for configuration
### Create R2 API Token
1. Go to R2 > Overview > Manage R2 API Tokens
2. Click "Create API token"
3. Set permissions: "Object Read & Write"
4. Optionally restrict to specific bucket
5. Copy the Access Key ID and Secret Access Key
### Configure CORS (if needed for direct uploads)
In R2 bucket settings, add CORS policy:
```json
[
{
"AllowedOrigins": ["https://your-domain.com"],
"AllowedMethods": ["GET", "PUT", "POST", "DELETE"],
"AllowedHeaders": ["*"],
"MaxAgeSeconds": 3600
}
]
Environment Variables
Set these in Dokploy:
| Variable | Example | Description |
|---|---|---|
S3_ENDPOINT |
https://abc123.r2.cloudflarestorage.com |
Your R2 endpoint |
S3_ACCESS_KEY_ID |
abc123... |
R2 API access key |
S3_SECRET_ACCESS_KEY |
xyz789... |
R2 API secret key |
S3_BUCKET |
myapp-storage |
Bucket name |
S3_REGION |
auto |
Always "auto" for R2 |
Cost Considerations
R2 pricing (as of 2024):
- Storage: $0.015/GB-month
- Class A operations (write): $4.50/million
- Class B operations (read): $0.36/million
- Egress: FREE (no data transfer fees)
---
## Quality Standards
### Mandatory Requirements
- [ ] R2 credentials use required variable syntax (`:?`)
- [ ] Endpoint format documented in comments
- [ ] CORS requirements noted for direct uploads
- [ ] README includes R2 setup instructions
- [ ] Alternative S3 providers noted if applicable
### Documentation Standards
- Include step-by-step R2 setup in README
- Document CORS configuration if needed
- Note cost considerations
- Provide alternative provider options
---
## Common Pitfalls
### Pitfall 1: Wrong endpoint format
**Issue**: Connection failures to R2
**Solution**: Use format `https://<ACCOUNT_ID>.r2.cloudflarestorage.com`
### Pitfall 2: Missing CORS for direct uploads
**Issue**: Browser upload failures
**Solution**: Configure CORS on R2 bucket
### Pitfall 3: S3_FORCE_PATH_STYLE wrong
**Issue**: Bucket not found errors
**Solution**: Use `"false"` for R2, `"true"` for MinIO
### Pitfall 4: Region mismatch
**Issue**: Signature errors
**Solution**: Use `S3_REGION: auto` for R2
---
## Integration
### Skills-First Approach (v2.0+)
This skill is part of the **skills-first architecture** - loaded during Generation phase when Cloudflare services (R2, DNS challenge, Zero Trust) are needed.
### Related Skills
- `dokploy-environment-config`: Environment variable patterns
- `dokploy-traefik-routing`: DNS challenge configuration
- `dokploy-security-hardening`: Zero Trust patterns
### Invoked By
- `/dokploy-create` command: Phase 3 (Generation) - Step 4 (when CF services detected)
### Order in Workflow (Progressive Loading)
1. `dokploy-compose-structure`: Create base structure
2. `dokploy-traefik-routing`: Add routing labels
3. `dokploy-health-patterns`: Add health checks
4. **This skill**: Add Cloudflare integration (Step 4, if applicable)
5. `dokploy-environment-config`: Configure environment
6. `dokploy-template-toml`: Create template.toml
See: `.claude/commands/dokploy-create.md` for full workflow