update skills

This commit is contained in:
2026-03-17 16:53:22 -07:00
parent 0b0783ef8e
commit f9a530667e
389 changed files with 54512 additions and 1 deletions

View File

@@ -0,0 +1,129 @@
# Cloudflare Tunnel
Secure outbound-only connections between infrastructure and Cloudflare's global network.
## Overview
Cloudflare Tunnel (formerly Argo Tunnel) enables:
- **Outbound-only connections** - No inbound ports or firewall changes
- **Public hostname routing** - Expose local services to internet
- **Private network access** - Connect internal networks via WARP
- **Zero Trust integration** - Built-in access policies
**Architecture**: Tunnel (persistent object) → Replica (`cloudflared` process) → Origin services
**Terminology:**
- **Tunnel**: Named persistent object with UUID
- **Replica**: Individual `cloudflared` process connected to tunnel
- **Config Source**: Where ingress rules stored (local file vs Cloudflare dashboard)
- **Connector**: Legacy term for replica
## Quick Start
### Local Config
```bash
# Install cloudflared
brew install cloudflared # macOS
# Authenticate
cloudflared tunnel login
# Create tunnel
cloudflared tunnel create my-tunnel
# Route DNS
cloudflared tunnel route dns my-tunnel app.example.com
# Run tunnel
cloudflared tunnel run my-tunnel
```
### Dashboard Config (Recommended)
1. **Zero Trust** > **Networks** > **Tunnels** > **Create**
2. Name tunnel, copy token
3. Configure routes in dashboard
4. Run: `cloudflared tunnel --no-autoupdate run --token <TOKEN>`
## Decision Tree
**Choose config source:**
```
Need centralized config updates?
├─ Yes → Token-based (dashboard config)
└─ No → Local config file
Multiple environments (dev/staging/prod)?
├─ Yes → Local config (version controlled)
└─ No → Either works
Need firewall approval?
└─ See networking.md first
```
## Core Commands
```bash
# Tunnel lifecycle
cloudflared tunnel create <name>
cloudflared tunnel list
cloudflared tunnel info <name>
cloudflared tunnel delete <name>
# DNS routing
cloudflared tunnel route dns <tunnel> <hostname>
cloudflared tunnel route list
# Private network
cloudflared tunnel route ip add 10.0.0.0/8 <tunnel>
# Run tunnel
cloudflared tunnel run <name>
```
## Configuration Example
```yaml
# ~/.cloudflared/config.yml
tunnel: 6ff42ae2-765d-4adf-8112-31c55c1551ef
credentials-file: /root/.cloudflared/6ff42ae2-765d-4adf-8112-31c55c1551ef.json
ingress:
- hostname: app.example.com
service: http://localhost:8000
- hostname: api.example.com
service: https://localhost:8443
originRequest:
noTLSVerify: true
- service: http_status:404
```
## Reading Order
**New to Cloudflare Tunnel:**
1. This README (overview, quick start)
2. [networking.md](./networking.md) - Firewall rules, connectivity pre-checks
3. [configuration.md](./configuration.md) - Config file options, ingress rules
4. [patterns.md](./patterns.md) - Docker, Kubernetes, production deployment
5. [gotchas.md](./gotchas.md) - Troubleshooting, best practices
**Enterprise deployment:**
1. [networking.md](./networking.md) - Corporate firewall requirements
2. [gotchas.md](./gotchas.md) - HA setup, security best practices
3. [patterns.md](./patterns.md) - Kubernetes, rolling updates
**Programmatic control:**
1. [api.md](./api.md) - REST API, TypeScript SDK
## In This Reference
- [networking.md](./networking.md) - Firewall rules, ports, connectivity pre-checks
- [configuration.md](./configuration.md) - Config file options, ingress rules, TLS settings
- [api.md](./api.md) - REST API, TypeScript SDK, token-based tunnels
- [patterns.md](./patterns.md) - Docker, Kubernetes, Terraform, HA, use cases
- [gotchas.md](./gotchas.md) - Troubleshooting, limitations, best practices
## See Also
- [workers](../workers/) - Workers with Tunnel integration
- [access](../access/) - Zero Trust access policies
- [warp](../warp/) - WARP client for private networks

View File

@@ -0,0 +1,193 @@
# Tunnel API
## Cloudflare API Access
**Base URL**: `https://api.cloudflare.com/client/v4`
**Authentication**:
```bash
Authorization: Bearer ${CF_API_TOKEN}
```
## TypeScript SDK
Install: `npm install cloudflare`
```typescript
import Cloudflare from 'cloudflare';
const cf = new Cloudflare({
apiToken: process.env.CF_API_TOKEN,
});
const accountId = process.env.CF_ACCOUNT_ID;
```
## Create Tunnel
### cURL
```bash
curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
--data '{
"name": "my-tunnel",
"tunnel_secret": "<base64-secret>"
}'
```
### TypeScript
```typescript
const tunnel = await cf.zeroTrust.tunnels.create({
account_id: accountId,
name: 'my-tunnel',
tunnel_secret: Buffer.from(crypto.randomBytes(32)).toString('base64'),
});
console.log(`Tunnel ID: ${tunnel.id}`);
```
## List Tunnels
### cURL
```bash
curl -X GET "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels" \
-H "Authorization: Bearer ${CF_API_TOKEN}"
```
### TypeScript
```typescript
const tunnels = await cf.zeroTrust.tunnels.list({
account_id: accountId,
});
for (const tunnel of tunnels.result) {
console.log(`${tunnel.name}: ${tunnel.id}`);
}
```
## Get Tunnel Info
### cURL
```bash
curl -X GET "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels/{tunnel_id}" \
-H "Authorization: Bearer ${CF_API_TOKEN}"
```
### TypeScript
```typescript
const tunnel = await cf.zeroTrust.tunnels.get(tunnelId, {
account_id: accountId,
});
console.log(`Status: ${tunnel.status}`);
console.log(`Connections: ${tunnel.connections?.length || 0}`);
```
## Update Tunnel Config
### cURL
```bash
curl -X PUT "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels/{tunnel_id}/configurations" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
-H "Content-Type: application/json" \
--data '{
"config": {
"ingress": [
{"hostname": "app.example.com", "service": "http://localhost:8000"},
{"service": "http_status:404"}
]
}
}'
```
### TypeScript
```typescript
const config = await cf.zeroTrust.tunnels.configurations.update(
tunnelId,
{
account_id: accountId,
config: {
ingress: [
{ hostname: 'app.example.com', service: 'http://localhost:8000' },
{ service: 'http_status:404' },
],
},
}
);
```
## Delete Tunnel
### cURL
```bash
curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels/{tunnel_id}" \
-H "Authorization: Bearer ${CF_API_TOKEN}"
```
### TypeScript
```typescript
await cf.zeroTrust.tunnels.delete(tunnelId, {
account_id: accountId,
});
```
## Token-Based Tunnels (Config Source: Cloudflare)
Token-based tunnels store config in Cloudflare dashboard instead of local files.
### Via Dashboard
1. **Zero Trust** > **Networks** > **Tunnels**
2. **Create a tunnel** > **Cloudflared**
3. Configure routes in dashboard
4. Copy token
5. Run on origin:
```bash
cloudflared service install <TOKEN>
```
### Via Token
```bash
# Run with token (no config file needed)
cloudflared tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}
# Docker
docker run cloudflare/cloudflared:latest tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}
```
### Get Tunnel Token (TypeScript)
```typescript
// Get tunnel to retrieve token
const tunnel = await cf.zeroTrust.tunnels.get(tunnelId, {
account_id: accountId,
});
// Token available in tunnel.token (only for config source: cloudflare)
const token = tunnel.token;
```
## DNS Routes API
```bash
# Create DNS route
curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels/{tunnel_id}/connections" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
--data '{"hostname": "app.example.com"}'
# Delete route
curl -X DELETE "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels/{tunnel_id}/connections/{route_id}" \
-H "Authorization: Bearer ${CF_API_TOKEN}"
```
## Private Network Routes API
```bash
# Add IP route
curl -X POST "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels/{tunnel_id}/routes" \
-H "Authorization: Bearer ${CF_API_TOKEN}" \
--data '{"ip_network": "10.0.0.0/8"}'
# List IP routes
curl -X GET "https://api.cloudflare.com/client/v4/accounts/{account_id}/tunnels/{tunnel_id}/routes" \
-H "Authorization: Bearer ${CF_API_TOKEN}"
```

View File

@@ -0,0 +1,157 @@
# Tunnel Configuration
## Config Source
Tunnels use one of two config sources:
| Config Source | Storage | Updates | Use Case |
|---------------|---------|---------|----------|
| Local | `config.yml` file | Edit file, restart | Dev, multi-env, version control |
| Cloudflare | Dashboard/API | Instant, no restart | Production, centralized management |
**Token-based tunnels** = config source: Cloudflare
**Locally-managed tunnels** = config source: local
## Config File Location
```
~/.cloudflared/config.yml # User config
/etc/cloudflared/config.yml # System-wide (Linux)
```
## Basic Structure
```yaml
tunnel: <UUID>
credentials-file: /path/to/<UUID>.json
ingress:
- hostname: app.example.com
service: http://localhost:8000
- service: http_status:404 # Required catch-all
```
## Ingress Rules
Rules evaluated **top to bottom**, first match wins.
```yaml
ingress:
# Exact hostname + path regex
- hostname: static.example.com
path: \.(jpg|png|css|js)$
service: https://localhost:8001
# Wildcard hostname
- hostname: "*.example.com"
service: https://localhost:8002
# Path only (all hostnames)
- path: /api/.*
service: http://localhost:9000
# Catch-all (required)
- service: http_status:404
```
**Validation**:
```bash
cloudflared tunnel ingress validate
cloudflared tunnel ingress rule https://foo.example.com
```
## Service Types
| Protocol | Format | Client Requirement |
|----------|--------|-------------------|
| HTTP | `http://localhost:8000` | Browser |
| HTTPS | `https://localhost:8443` | Browser |
| TCP | `tcp://localhost:2222` | `cloudflared access tcp` |
| SSH | `ssh://localhost:22` | `cloudflared access ssh` |
| RDP | `rdp://localhost:3389` | `cloudflared access rdp` |
| Unix | `unix:/path/to/socket` | Browser |
| Test | `hello_world` | Browser |
## Origin Configuration
### Connection Settings
```yaml
originRequest:
connectTimeout: 30s
tlsTimeout: 10s
tcpKeepAlive: 30s
keepAliveTimeout: 90s
keepAliveConnections: 100
```
### TLS Settings
```yaml
originRequest:
noTLSVerify: true # Disable cert verification
originServerName: "app.internal" # Override SNI
caPool: /path/to/ca.pem # Custom CA
```
### HTTP Settings
```yaml
originRequest:
disableChunkedEncoding: true
httpHostHeader: "app.internal"
http2Origin: true
```
## Private Network Mode
```yaml
tunnel: <UUID>
credentials-file: /path/to/creds.json
warp-routing:
enabled: true
```
```bash
cloudflared tunnel route ip add 10.0.0.0/8 my-tunnel
cloudflared tunnel route ip add 192.168.1.100/32 my-tunnel
```
## Config Source Comparison
### Local Config
```yaml
# config.yml
tunnel: <UUID>
credentials-file: /path/to/<UUID>.json
ingress:
- hostname: app.example.com
service: http://localhost:8000
- service: http_status:404
```
```bash
cloudflared tunnel run my-tunnel
```
**Pros:** Version control, multi-environment, offline edits
**Cons:** Requires file distribution, manual restarts
### Cloudflare Config (Token-Based)
```bash
# No config file needed
cloudflared tunnel --no-autoupdate run --token <TOKEN>
```
Configure routes in dashboard: **Zero Trust** > **Networks** > **Tunnels** > [Tunnel] > **Public Hostname**
**Pros:** Centralized updates, no file management, instant route changes
**Cons:** Requires dashboard/API access, less portable
## Environment Variables
```bash
TUNNEL_TOKEN=<token> # Token for config source: cloudflare
TUNNEL_ORIGIN_CERT=/path/to/cert.pem # Override cert path (local config)
NO_AUTOUPDATE=true # Disable auto-updates
TUNNEL_LOGLEVEL=debug # Log level
```

View File

@@ -0,0 +1,147 @@
# Tunnel Gotchas
## Common Errors
### "Error 1016 (Origin DNS Error)"
**Cause:** Tunnel not running or not connected
**Solution:**
```bash
cloudflared tunnel info my-tunnel # Check status
ps aux | grep cloudflared # Verify running
journalctl -u cloudflared -n 100 # Check logs
```
### "Self-signed certificate rejected"
**Cause:** Origin using self-signed certificate
**Solution:**
```yaml
originRequest:
noTLSVerify: true # Dev only
caPool: /path/to/ca.pem # Custom CA
```
### "Connection timeout"
**Cause:** Origin slow to respond or timeout settings too low
**Solution:**
```yaml
originRequest:
connectTimeout: 60s
tlsTimeout: 20s
keepAliveTimeout: 120s
```
### "Tunnel not starting"
**Cause:** Invalid config, missing credentials, or tunnel doesn't exist
**Solution:**
```bash
cloudflared tunnel ingress validate # Validate config
ls -la ~/.cloudflared/*.json # Verify credentials
cloudflared tunnel list # Verify tunnel exists
```
### "Connection already registered"
**Cause:** Multiple replicas with same connector ID or stale connection
**Solution:**
```bash
# Check active connections
cloudflared tunnel info my-tunnel
# Wait 60s for stale connection cleanup, or restart with new connector ID
cloudflared tunnel run my-tunnel
```
### "Tunnel credentials rotated but connections fail"
**Cause:** Old cloudflared processes using expired credentials
**Solution:**
```bash
# Stop all cloudflared processes
pkill cloudflared
# Verify stopped
ps aux | grep cloudflared
# Restart with new credentials
cloudflared tunnel run my-tunnel
```
## Limits
| Resource/Limit | Value | Notes |
|----------------|-------|-------|
| Free tier | Unlimited tunnels | Unlimited traffic |
| Tunnel replicas | 1000 per tunnel | Max concurrent |
| Connection duration | No hard limit | Hours to days |
| Long-lived connections | May drop during updates | WebSocket, SSH, UDP |
| Replica registration | ~5s TTL | Old replica dropped after 5s no heartbeat |
| Token rotation grace | 24 hours | Old tokens work during grace period |
## Best Practices
### Security
1. Use token-based tunnels (config source: cloudflare) for centralized control
2. Enable Access policies for sensitive services
3. Rotate tunnel credentials regularly
4. After rotation: stop all old cloudflared processes within 24h grace period
5. Verify TLS certs (`noTLSVerify: false`)
6. Restrict `bastion` service type
### Performance
1. Run multiple replicas for HA (2-4 typical, load balanced automatically)
2. Replicas share same tunnel UUID, get unique connector IDs
3. Place `cloudflared` close to origin (same network)
4. Use HTTP/2 for gRPC (`http2Origin: true`)
5. Tune keepalive for long-lived connections
6. Monitor connection counts
### Configuration
1. Use environment variables for secrets
2. Version control config files
3. Validate before deploying (`cloudflared tunnel ingress validate`)
4. Test rules (`cloudflared tunnel ingress rule <URL>`)
5. Document rule order (first match wins)
### Operations
1. Monitor tunnel health in dashboard (shows active replicas)
2. Set up disconnect alerts (when replica count drops to 0)
3. Graceful shutdown for config updates
4. Update replicas in rolling fashion (update 1, wait, update next)
5. Keep `cloudflared` updated (1 year support window)
6. Use `--no-autoupdate` in prod; control updates manually
## Debug Mode
```bash
cloudflared tunnel --loglevel debug run my-tunnel
cloudflared tunnel ingress rule https://app.example.com
```
## Migration Strategies
### From Ngrok
```yaml
# Ngrok: ngrok http 8000
# Cloudflare Tunnel:
ingress:
- hostname: app.example.com
service: http://localhost:8000
- service: http_status:404
```
### From VPN
```yaml
# Replace VPN with private network routing
warp-routing:
enabled: true
```
```bash
cloudflared tunnel route ip add 10.0.0.0/8 my-tunnel
```
Users install WARP client instead of VPN.

View File

@@ -0,0 +1,168 @@
# Tunnel Networking
## Connectivity Requirements
### Outbound Ports
Cloudflared requires outbound access on:
| Port | Protocol | Purpose | Required |
|------|----------|---------|----------|
| 7844 | TCP/UDP | Primary tunnel protocol (QUIC) | Yes |
| 443 | TCP | Fallback (HTTP/2) | Yes |
**Network path:**
```
cloudflared → edge.argotunnel.com:7844 (preferred)
cloudflared → region.argotunnel.com:443 (fallback)
```
### Firewall Rules
#### Minimal (Production)
```bash
# Outbound only
ALLOW tcp/udp 7844 to *.argotunnel.com
ALLOW tcp 443 to *.argotunnel.com
```
#### Full (Recommended)
```bash
# Tunnel connectivity
ALLOW tcp/udp 7844 to *.argotunnel.com
ALLOW tcp 443 to *.argotunnel.com
# API access (for token-based tunnels)
ALLOW tcp 443 to api.cloudflare.com
# Updates (optional)
ALLOW tcp 443 to github.com
ALLOW tcp 443 to objects.githubusercontent.com
```
### IP Ranges
Cloudflare Anycast IPs (tunnel endpoints):
```
# IPv4
198.41.192.0/24
198.41.200.0/24
# IPv6
2606:4700::/32
```
**Note:** Use DNS resolution for `*.argotunnel.com` rather than hardcoding IPs. Cloudflare may add edge locations.
## Pre-Flight Check
Test connectivity before deploying:
```bash
# Test DNS resolution
dig edge.argotunnel.com +short
# Test port 7844 (QUIC/UDP)
nc -zvu edge.argotunnel.com 7844
# Test port 443 (HTTP/2 fallback)
nc -zv edge.argotunnel.com 443
# Test with cloudflared
cloudflared tunnel --loglevel debug run my-tunnel
# Look for "Registered tunnel connection"
```
### Common Connectivity Errors
| Error | Cause | Solution |
|-------|-------|----------|
| "no such host" | DNS blocked | Allow port 53 UDP/TCP |
| "context deadline exceeded" | Port 7844 blocked | Allow UDP/TCP 7844 |
| "TLS handshake timeout" | Port 443 blocked | Allow TCP 443, disable SSL inspection |
## Protocol Selection
Cloudflared automatically selects protocol:
| Protocol | Port | Priority | Use Case |
|----------|------|----------|----------|
| QUIC | 7844 UDP | 1st (preferred) | Low latency, best performance |
| HTTP/2 | 443 TCP | 2nd (fallback) | QUIC blocked by firewall |
**Force HTTP/2 fallback:**
```bash
cloudflared tunnel --protocol http2 run my-tunnel
```
**Verify active protocol:**
```bash
cloudflared tunnel info my-tunnel
# Shows "connections" with protocol type
```
## Private Network Routing
### WARP Client Requirements
Users accessing private IPs via WARP need:
```bash
# Outbound (WARP client)
ALLOW udp 500,4500 to 162.159.*.* (IPsec)
ALLOW udp 2408 to 162.159.*.* (WireGuard)
ALLOW tcp 443 to *.cloudflareclient.com
```
### Split Tunnel Configuration
Route only private networks through tunnel:
```yaml
# warp-routing config
warp-routing:
enabled: true
```
```bash
# Add specific routes
cloudflared tunnel route ip add 10.0.0.0/8 my-tunnel
cloudflared tunnel route ip add 172.16.0.0/12 my-tunnel
cloudflared tunnel route ip add 192.168.0.0/16 my-tunnel
```
WARP users can access these IPs without VPN.
## Network Diagnostics
### Connection Diagnostics
```bash
# Check edge selection and connection health
cloudflared tunnel info my-tunnel --output json | jq '.connections[]'
# Enable metrics endpoint
cloudflared tunnel --metrics localhost:9090 run my-tunnel
curl localhost:9090/metrics | grep cloudflared_tunnel
# Test latency
curl -w "time_total: %{time_total}\n" -o /dev/null https://myapp.example.com
```
## Corporate Network Considerations
Cloudflared honors proxy environment variables (`HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`).
If corporate proxy intercepts TLS, add corporate root CA to system trust store.
## Bandwidth and Rate Limits
| Limit | Value | Notes |
|-------|-------|-------|
| Request size | 100 MB | Single HTTP request |
| Upload speed | No hard limit | Governed by network/plan |
| Concurrent connections | 1000 per tunnel | Across all replicas |
| Requests per second | No limit | Subject to DDoS detection |
**Large file transfers:**
Use R2 or Workers with chunked uploads instead of streaming through tunnel.

View File

@@ -0,0 +1,192 @@
# Tunnel Patterns
## Docker Deployment
### Token-Based (Recommended)
```yaml
services:
cloudflared:
image: cloudflare/cloudflared:latest
command: tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}
restart: unless-stopped
```
### Local Config
```yaml
services:
cloudflared:
image: cloudflare/cloudflared:latest
volumes:
- ./config.yml:/etc/cloudflared/config.yml:ro
- ./credentials.json:/etc/cloudflared/credentials.json:ro
command: tunnel run
```
## Kubernetes Deployment
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: cloudflared
spec:
replicas: 2
selector:
matchLabels:
app: cloudflared
template:
metadata:
labels:
app: cloudflared
spec:
containers:
- name: cloudflared
image: cloudflare/cloudflared:latest
args:
- tunnel
- --no-autoupdate
- run
- --token
- $(TUNNEL_TOKEN)
env:
- name: TUNNEL_TOKEN
valueFrom:
secretKeyRef:
name: tunnel-credentials
key: token
```
## High Availability
```yaml
# Same config on multiple servers
tunnel: <UUID>
credentials-file: /path/to/creds.json
ingress:
- hostname: app.example.com
service: http://localhost:8000
- service: http_status:404
```
Run same config on multiple machines. Cloudflare automatically load balances. Long-lived connections (WebSocket, SSH) may drop during updates.
## Use Cases
### Web Application
```yaml
ingress:
- hostname: myapp.example.com
service: http://localhost:3000
- service: http_status:404
```
### SSH Access
```yaml
ingress:
- hostname: ssh.example.com
service: ssh://localhost:22
- service: http_status:404
```
Client: `cloudflared access ssh --hostname ssh.example.com`
### gRPC Service
```yaml
ingress:
- hostname: grpc.example.com
service: http://localhost:50051
originRequest:
http2Origin: true
- service: http_status:404
```
## Infrastructure as Code
### Terraform
```hcl
resource "random_id" "tunnel_secret" {
byte_length = 32
}
resource "cloudflare_tunnel" "app" {
account_id = var.cloudflare_account_id
name = "app-tunnel"
secret = random_id.tunnel_secret.b64_std
}
resource "cloudflare_tunnel_config" "app" {
account_id = var.cloudflare_account_id
tunnel_id = cloudflare_tunnel.app.id
config {
ingress_rule {
hostname = "app.example.com"
service = "http://localhost:8000"
}
ingress_rule { service = "http_status:404" }
}
}
resource "cloudflare_record" "app" {
zone_id = var.cloudflare_zone_id
name = "app"
value = cloudflare_tunnel.app.cname
type = "CNAME"
proxied = true
}
output "tunnel_token" {
value = cloudflare_tunnel.app.tunnel_token
sensitive = true
}
```
### Pulumi
```typescript
import * as cloudflare from "@pulumi/cloudflare";
import * as random from "@pulumi/random";
const secret = new random.RandomId("secret", { byteLength: 32 });
const tunnel = new cloudflare.ZeroTrustTunnelCloudflared("tunnel", {
accountId: accountId,
name: "app-tunnel",
secret: secret.b64Std,
});
const config = new cloudflare.ZeroTrustTunnelCloudflaredConfig("config", {
accountId: accountId,
tunnelId: tunnel.id,
config: {
ingressRules: [
{ hostname: "app.example.com", service: "http://localhost:8000" },
{ service: "http_status:404" },
],
},
});
new cloudflare.Record("dns", {
zoneId: zoneId,
name: "app",
value: tunnel.cname,
type: "CNAME",
proxied: true,
});
```
## Service Installation
### Linux systemd
```bash
cloudflared service install
systemctl start cloudflared && systemctl enable cloudflared
journalctl -u cloudflared -f # Logs
```
### macOS launchd
```bash
sudo cloudflared service install
sudo launchctl start com.cloudflare.cloudflared
```