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.