Penpot logo

Penpot

Open-source design and prototyping platform

Alternative to: Figma, Sketch, Adobe XD, InVision


Penpot is the open-source design platform for teams that build digital products at scale. It gives designers and developers full ownership of their design infrastructure, combining vector editing, prototyping, and real-time collaboration on open web standards like SVG, CSS, and JSON. Because it works entirely in the browser, teams can self-host their own design environment without relying on a proprietary vendor.

Penpot Docker Compose example

Self-host Penpot on your own server, homelab, or VPS starting from this Docker Compose example. It runs Penpot in Docker containers using the official penpotapp/frontend:latest, penpotapp/backend:latest, penpotapp/mcp:latest, penpotapp/exporter:latest, postgres:15, valkey/valkey:8.1, sj26/mailcatcher:latest images, with persistent volumes and automatic restarts preconfigured. Review the environment variables and adjust them to your setup, save the file as compose.yml (or docker-compose.yml), and start the stack with docker compose up -d.

networks:
  penpot:

volumes:
  penpot_postgres_v15:
  penpot_assets:

services:
  penpot-frontend:
    image: penpotapp/frontend:latest
    restart: unless-stopped
    volumes:
      - penpot_assets:/opt/data/assets
    depends_on:
      - penpot-backend
      - penpot-exporter
      - penpot-mcp
    networks:
      - penpot
    environment:
      # Feature flags. Remove "disable-secure-session-cookies" and "disable-email-verification" when exposing Penpot to the internet with a real SMTP server.
      PENPOT_FLAGS: "disable-email-verification enable-smtp enable-prepl-server disable-secure-session-cookies enable-mcp"
      # Max upload body size in bytes.
      PENPOT_HTTP_SERVER_MAX_BODY_SIZE: "367001600"
      # Deprecated alias of the max body size, kept for compatibility.
      PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE: "367001600"
      # Public URL where users reach Penpot, including scheme (e.g. https://penpot.example.com).
      PENPOT_PUBLIC_URI: "http://localhost:9001"

  penpot-backend:
    image: penpotapp/backend:latest
    restart: unless-stopped
    volumes:
      - penpot_assets:/opt/data/assets
    depends_on:
      penpot-postgres:
        condition: service_healthy
      penpot-valkey:
        condition: service_healthy
    networks:
      - penpot
    environment:
      # Feature flags (see the frontend service for details).
      PENPOT_FLAGS: "disable-email-verification enable-smtp enable-prepl-server disable-secure-session-cookies enable-mcp"
      # Public URL where users reach Penpot, including scheme.
      PENPOT_PUBLIC_URI: "http://localhost:9001"
      # Max upload body size in bytes.
      PENPOT_HTTP_SERVER_MAX_BODY_SIZE: "367001600"
      # Deprecated alias of the max body size, kept for compatibility.
      PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE: "367001600"
      # Master secret key from which subsystem keys (sessions, invitations) are derived. Use a long random string.
      PENPOT_SECRET_KEY: "changeme"
      # PostgreSQL connection URI (points at the bundled postgres service).
      PENPOT_DATABASE_URI: "postgresql://penpot-postgres/penpot"
      # PostgreSQL user (must match the postgres service).
      PENPOT_DATABASE_USERNAME: "penpot"
      # PostgreSQL password (internal to the compose network; must match the postgres service).
      PENPOT_DATABASE_PASSWORD: "penpot"
      # Valkey/Redis URI used for websocket notifications.
      PENPOT_REDIS_URI: "redis://penpot-valkey/0"
      # Asset storage backend: filesystem (fs) stores uploads in the penpot_assets volume.
      PENPOT_OBJECTS_STORAGE_BACKEND: "fs"
      # Directory used by the fs storage backend.
      PENPOT_OBJECTS_STORAGE_FS_DIRECTORY: "/opt/data/assets"
      # Whether to send anonymous usage telemetry to the Penpot team.
      PENPOT_TELEMETRY_ENABLED: "true"
      # Telemetry referer tag.
      PENPOT_TELEMETRY_REFERER: "compose"
      # Default From address on outgoing emails.
      PENPOT_SMTP_DEFAULT_FROM: "no-reply@example.com"
      # Default Reply-To address on outgoing emails.
      PENPOT_SMTP_DEFAULT_REPLY_TO: "no-reply@example.com"
      # SMTP host. Defaults to the bundled mailcatch service; point at a real SMTP server for production.
      PENPOT_SMTP_HOST: "penpot-mailcatch"
      # SMTP port.
      PENPOT_SMTP_PORT: "1025"
      # SMTP username.
      PENPOT_SMTP_USERNAME: ""
      # SMTP password.
      PENPOT_SMTP_PASSWORD: ""
      # Whether to use STARTTLS for SMTP (true/false).
      PENPOT_SMTP_TLS: "false"
      # Whether to use SSL for SMTP (true/false).
      PENPOT_SMTP_SSL: "false"

  penpot-mcp:
    image: penpotapp/mcp:latest
    restart: unless-stopped
    networks:
      - penpot

  penpot-exporter:
    image: penpotapp/exporter:latest
    restart: unless-stopped
    depends_on:
      penpot-valkey:
        condition: service_healthy
    networks:
      - penpot
    environment:
      # Master secret key — must match the backend service.
      PENPOT_SECRET_KEY: "changeme"
      # Public URL where users reach Penpot, including scheme.
      PENPOT_PUBLIC_URI: "http://localhost:9001"
      # Internal URL the exporter uses to reach the frontend on the compose network. Don't change.
      PENPOT_INTERNAL_URI: "http://penpot-frontend:8080"
      # Valkey/Redis URI used for websocket notifications.
      PENPOT_REDIS_URI: "redis://penpot-valkey/0"

  penpot-postgres:
    image: postgres:15
    restart: unless-stopped
    stop_signal: SIGINT
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U penpot"]
      interval: 2s
      timeout: 10s
      retries: 5
      start_period: 2s
    volumes:
      - penpot_postgres_v15:/var/lib/postgresql/data
    networks:
      - penpot
    environment:
      POSTGRES_INITDB_ARGS: "--data-checksums"
      POSTGRES_DB: "penpot"
      POSTGRES_USER: "penpot"
      POSTGRES_PASSWORD: "penpot"

  penpot-valkey:
    image: valkey/valkey:8.1
    restart: unless-stopped
    healthcheck:
      test: ["CMD-SHELL", "valkey-cli ping | grep PONG"]
      interval: 1s
      timeout: 3s
      retries: 5
      start_period: 3s
    networks:
      - penpot

  penpot-mailcatch:
    image: sj26/mailcatcher:latest
    restart: unless-stopped
    expose:
      - "1025"
    networks:
      - penpot

Values set to changeme are required — replace them with your own values before starting Penpot.

Prefer a managed setup? WinterFlow installs, configures, and updates Penpot for you using this same Docker Compose configuration.