强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Caddy 从入门到精通 / 09 - Docker 部署 / Docker Deployment

Docker 部署 / Docker Deployment

容器化是 Caddy 最常见的部署方式。官方镜像体积小(~40MB),开箱即用。

Containerization is the most common deployment method for Caddy. The official image is small (~40MB) and works out of the box.


🟢 基础 / Basics

最简 Docker 运行

docker run -d \
    -p 80:80 \
    -p 443:443 \
    -v ./Caddyfile:/etc/caddy/Caddyfile \
    -v caddy_data:/data \
    -v caddy_config:/config \
    caddy:2

三个挂载点:

挂载用途
/etc/caddy/Caddyfile配置文件
/data证书和 ACME 状态(必须持久化
/config运行时配置缓存

Docker Compose 基础版

# docker-compose.yml
services:
  caddy:
    image: caddy:2
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config

volumes:
  caddy_data:
  caddy_config:
# Caddyfile
example.com {
    respond "Hello from Docker!"
}
docker compose up -d

查看日志

docker compose logs -f caddy

🟡 进阶 / Intermediate

Docker Compose:Caddy + 应用

services:
  caddy:
    image: caddy:2
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
    depends_on:
      - app

  app:
    image: node:20-alpine
    working_dir: /app
    volumes:
      - ./app:/app
    command: ["node", "server.js"]
    expose:
      - "3000"

volumes:
  caddy_data:
  caddy_config:
example.com {
    reverse_proxy app:3000
}

Caddy 通过 Docker 网络直接以服务名 app 访问后端。

多站点 Docker Compose

services:
  caddy:
    image: caddy:2
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - ./sites:/srv
      - caddy_data:/data
      - caddy_config:/config

  api:
    image: golang:1.22
    # ...

  blog:
    image: wordpress:latest
    # ...

volumes:
  caddy_data:
  caddy_config:
example.com {
    reverse_proxy api:8080
}

blog.example.com {
    reverse_proxy blog:80
}

static.example.com {
    root * /srv/static
    file_server
}

传递环境变量

services:
  caddy:
    image: caddy:2
    environment:
      - DOMAIN=example.com
      - APP_PORT=3000
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy_data:/data
      - caddy_config:/config
{$DOMAIN:localhost} {
    reverse_proxy app:{$APP_PORT:3000}
}

{$VAR:default} 语法:读取环境变量,不存在时使用默认值。

挂载静态文件目录

services:
  caddy:
    image: caddy:2
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - ./public:/srv:ro
      - caddy_data:/data
      - caddy_config:/config
example.com {
    root * /srv
    file_server
}

自定义 Caddy 镜像(带插件)

FROM caddy:2-builder AS builder
RUN xcaddy build \
    --with github.com/caddyserver/transform-encoder \
    --with github.com/mholt/caddy-ratelimit

FROM caddy:2
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
docker build -t my-caddy .

TLS 证书持久化

⚠️ 关键: 不持久化 /data 会导致每次重建容器时重新申请证书,可能触发 Let’s Encrypt 速率限制。

volumes:
  caddy_data:
    driver: local
    driver_opts:
      type: none
      o: bind
      device: /opt/caddy/data

或使用 bind mount:

volumes:
  - /opt/caddy/data:/data
  - /opt/caddy/config:/config

🔴 高级 / Advanced

Caddy Docker Proxy(自动发现)

lucaslorentz/caddy-docker-proxy 让 Caddy 通过 Docker label 自动生成配置:

services:
  caddy:
    image: lucaslorentz/caddy-docker-proxy:ci-alpine
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - caddy_data:/data

  webapp:
    image: nginx:alpine
    labels:
      caddy: example.com
      caddy.reverse_proxy: "{{upstreams 80}}"

  api:
    image: node:20
    labels:
      caddy: api.example.com
      caddy.reverse_proxy: "{{upstreams 3000}}"
      caddy.encode: gzip

无需维护 Caddyfile,所有配置来自 Docker labels。

Docker Swarm 模式

services:
  caddy:
    image: lucaslorentz/caddy-docker-proxy:ci-alpine
    deploy:
      mode: global
      placement:
        constraints:
          - node.role == manager
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro

  webapp:
    image: nginx:alpine
    deploy:
      replicas: 3
    labels:
      caddy: example.com
      caddy.reverse_proxy: "{{upstreams 80}}"

在 Swarm 模式下,Caddy 自动发现所有副本并进行负载均衡。

Docker 网络隔离

services:
  caddy:
    image: caddy:2
    networks:
      - frontend
      - backend
    ports:
      - "80:80"
      - "443:443"

  app:
    image: node:20
    networks:
      - backend
    # 注意:app 不在 frontend 网络,无法被外部直接访问

  db:
    image: postgres:16
    networks:
      - backend
    # db 只在 backend 网络

networks:
  frontend:
  backend:
    internal: true  # 不允许外部访问

Caddy 同时在 frontendbackend 网络中,充当中间层。

健康检查 + 重启策略

services:
  caddy:
    image: caddy:2
    healthcheck:
      test: ["CMD", "caddy", "version"]
      interval: 30s
      timeout: 5s
      retries: 3
      start_period: 10s
    restart: unless-stopped

多阶段构建:前端 + Caddy

# 构建阶段
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

# 运行阶段
FROM caddy:2
COPY --from=builder /app/dist /srv
COPY Caddyfile /etc/caddy/Caddyfile

一个镜像包含前端构建结果和 Caddy,开箱即用。

容器内 Caddy 管理

# 进入容器
docker exec -it caddy-container sh

# 查看配置
caddy list-modules

# 重载配置(容器内)
caddy reload --config /etc/caddy/Caddyfile

# 验证配置
caddy validate --config /etc/caddy/Caddyfile

多容器共享证书

使用共享 volume 让多个 Caddy 实例共享证书:

services:
  caddy1:
    image: caddy:2
    volumes:
      - shared_certs:/data

  caddy2:
    image: caddy:2
    volumes:
      - shared_certs:/data

volumes:
  shared_certs:

小结 / Summary

层级内容
🟢 基础Docker run、基本 Compose、三个挂载点
🟡 进阶Compose + 应用、环境变量、自定义镜像、证书持久化
🔴 高级caddy-docker-proxy、Swarm、网络隔离、多阶段构建

下一章:高级主题 / Advanced Topics