Skip to content

Penpot

PVE 配置

配置建议值
系统Debian 12/13 或 Ubuntu 24.04 LTS
CPU4 vCPU,类型选择 host
内存8GB 固定内存
系统盘100GB,VirtIO SCSI、开启 Discard
网卡VirtIO,配置固定局域网 IP
QEMU Agent开启
GPU不需要直通

部署

sudo mkdir -p /opt/penpot
sudo chown "$USER":"$USER" /opt/penpot
cd /opt/penpot

curl -fsSLo docker-compose.yaml \
  https://raw.githubusercontent.com/penpot/penpot/main/docker/images/docker-compose.yaml

生成密钥:

sh
python3 -c "import secrets; print(secrets.token_urlsafe(64))"

python3 -c "import secrets; print(secrets.token_urlsafe(32))"

vim .env

sh
# 固定版本,升级时手动修改并检查发行说明。
PENPOT_VERSION=2.16

# 用户访问地址,必须和 Traefik 域名一致。
PENPOT_PUBLIC_URI=https://ui.lab.q123q.cc

# Penpot VM 的内网 IP。
PENPOT_LISTEN_IP=10.0.0.150

# 使用随机值替换。
PENPOT_SECRET_KEY=mgwadG--NXT4MRjE09CxuOLI6Xl22vcz5xMZpm-hJwhnboOxxUIR5GiBzkMbOGPQ3Z44rW4hb0P0WDcfZtZTQw
PENPOT_DATABASE_PASSWORD=_ssyPfi0IIgwSCRxpEq7wkTrAgezsIEP-ERBNTuyVrE

vim docker-compose.yml

yml
name: penpot

# ============================================================
# Penpot 公共配置
# ============================================================

x-penpot-common-env: &penpot-common-env
  # 【重点】必须是用户最终访问的 HTTPS 地址。
  # 登录回调、资源地址、邀请链接都会依赖它。
  PENPOT_PUBLIC_URI: "${PENPOT_PUBLIC_URI:?PENPOT_PUBLIC_URI is required}"

  # 【重点】
  # disable-email-verification:
  #   当前没有真实 SMTP,因此关闭邮箱验证。
  #
  # enable-log-emails / enable-log-invitation-tokens:
  #   邮件和邀请链接输出到 backend 日志。
  #
  # enable-prepl-server:
  #   允许通过 manage.py 创建和管理用户。
  #
  # enable-mcp:
  #   启用 Penpot MCP。
  #
  # HTTPS 环境不要添加 disable-secure-session-cookies。
  PENPOT_FLAGS: >-
    disable-email-verification
    enable-log-emails
    enable-log-invitation-tokens
    enable-prepl-server
    enable-mcp

  # 最大上传请求约 350 MiB。
  # 已删除废弃的 PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE。
  PENPOT_HTTP_SERVER_MAX_BODY_SIZE: "367001600"

x-penpot-secret-env: &penpot-secret-env
  # 【重点】必须保持不变,否则重启后会导致会话、邀请链接等失效。
  PENPOT_SECRET_KEY: "${PENPOT_SECRET_KEY:?PENPOT_SECRET_KEY is required}"


services:
  # ==========================================================
  # 对外入口
  # 外部 Traefik 将请求转发到该服务的 9001 端口。
  # ==========================================================
  penpot-frontend:
    image: "penpotapp/frontend:${PENPOT_VERSION:-2.16}"
    restart: unless-stopped

    ports:
      # 【重点】建议绑定到 Penpot VM 的内网 IP。
      # 另外应在防火墙中仅允许 Traefik 主机访问 9001。
      - "${PENPOT_LISTEN_IP:-0.0.0.0}:9001:8080"

    volumes:
      - penpot_assets:/opt/data/assets

    depends_on:
      - penpot-backend
      - penpot-exporter
      - penpot-mcp

    environment:
      <<: *penpot-common-env

    networks:
      - penpot


  # ==========================================================
  # Penpot 后端
  # ==========================================================
  penpot-backend:
    image: "penpotapp/backend:${PENPOT_VERSION:-2.16}"
    restart: unless-stopped

    volumes:
      # 【重点】用户上传的图片、字体和其他资源存放于此。
      - penpot_assets:/opt/data/assets

    depends_on:
      penpot-postgres:
        condition: service_healthy
      penpot-valkey:
        condition: service_healthy

    environment:
      <<: [*penpot-common-env, *penpot-secret-env]

      # PostgreSQL
      PENPOT_DATABASE_URI: postgresql://penpot-postgres/penpot
      PENPOT_DATABASE_USERNAME: penpot
      PENPOT_DATABASE_PASSWORD: "${PENPOT_DATABASE_PASSWORD:?PENPOT_DATABASE_PASSWORD is required}"

      # Valkey 用于 WebSocket 通知和内部协调。
      PENPOT_REDIS_URI: redis://penpot-valkey/0

      # 使用 Docker Volume 保存上传资源。
      PENPOT_OBJECTS_STORAGE_BACKEND: fs
      PENPOT_OBJECTS_STORAGE_FS_DIRECTORY: /opt/data/assets

    networks:
      - penpot


  # ==========================================================
  # Penpot MCP
  # 需要保留 enable-mcp,并且当前只能运行单实例 Penpot。
  # ==========================================================
  penpot-mcp:
    image: "penpotapp/mcp:${PENPOT_VERSION:-2.16}"
    restart: unless-stopped

    networks:
      - penpot


  # ==========================================================
  # 文件导出服务
  # 用于 PNG、SVG、PDF 等导出,不能删除。
  # ==========================================================
  penpot-exporter:
    image: "penpotapp/exporter:${PENPOT_VERSION:-2.16}"
    restart: unless-stopped

    depends_on:
      penpot-valkey:
        condition: service_healthy

    environment:
      <<: *penpot-secret-env

      PENPOT_PUBLIC_URI: "${PENPOT_PUBLIC_URI:?PENPOT_PUBLIC_URI is required}"

      # 【重点】这是容器内部地址,不要改成公网域名。
      PENPOT_INTERNAL_URI: http://penpot-frontend:8080

      PENPOT_REDIS_URI: redis://penpot-valkey/0

    networks:
      - penpot


  # ==========================================================
  # PostgreSQL
  # ==========================================================
  penpot-postgres:
    image: postgres:15
    restart: unless-stopped
    stop_signal: SIGINT

    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U penpot -d penpot"]
      interval: 5s
      timeout: 5s
      retries: 10
      start_period: 10s

    environment:
      POSTGRES_INITDB_ARGS: --data-checksums
      POSTGRES_DB: penpot
      POSTGRES_USER: penpot
      POSTGRES_PASSWORD: "${PENPOT_DATABASE_PASSWORD:?PENPOT_DATABASE_PASSWORD is required}"

    volumes:
      # 【重点】这是最重要的备份对象之一。
      - penpot_postgres_v15:/var/lib/postgresql/data

    networks:
      - penpot


  # ==========================================================
  # Valkey
  # 临时状态和 WebSocket 协调服务,不需要额外持久化。
  # ==========================================================
  penpot-valkey:
    image: valkey/valkey:8.1
    restart: unless-stopped

    healthcheck:
      test: ["CMD-SHELL", "valkey-cli ping | grep PONG"]
      interval: 3s
      timeout: 3s
      retries: 10
      start_period: 5s

    environment:
      VALKEY_EXTRA_FLAGS: >-
        --maxmemory 128mb
        --maxmemory-policy volatile-lfu

    networks:
      - penpot


networks:
  penpot:
    name: penpot_internal


volumes:
  # 显式指定名称,避免修改 Compose 目录名后创建一套新的空卷。
  penpot_postgres_v15:
    name: penpot_postgres_v15

  penpot_assets:
    name: penpot_assets