# 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
```
