The stack

  • frontend — ClojureScript SPA served by nginx
  • backend — Clojure JVM application; REST + WebSocket API
  • exporter — Node + headless Chromium for rendering exports (PDF, PNG)
  • postgres — the data store
  • redis — pub/sub for collaboration events + caches

Install via docker compose

Penpot publishes an official compose file. Adapted minimally:

# docker-compose.yml
services:
  penpot-frontend:
    image: penpotapp/frontend:latest
    restart: unless-stopped
    ports:
      - "127.0.0.1:9001:80"
    volumes:
      - penpot-assets:/opt/data/assets
    depends_on: [ penpot-backend, penpot-exporter ]
    environment:
      PENPOT_FLAGS: enable-registration enable-login-with-password

  penpot-backend:
    image: penpotapp/backend:latest
    restart: unless-stopped
    volumes:
      - penpot-assets:/opt/data/assets
    depends_on: [ penpot-postgres, penpot-redis ]
    environment:
      PENPOT_FLAGS: enable-registration enable-login-with-password disable-email-verification enable-smtp
      PENPOT_PUBLIC_URI: https://design.example.com
      PENPOT_DATABASE_URI: postgresql://penpot-postgres/penpot
      PENPOT_DATABASE_USERNAME: penpot
      PENPOT_DATABASE_PASSWORD: ${DB_PASSWORD}
      PENPOT_REDIS_URI: redis://penpot-redis/0
      PENPOT_ASSETS_STORAGE_BACKEND: assets-fs
      PENPOT_STORAGE_ASSETS_FS_DIRECTORY: /opt/data/assets
      PENPOT_TELEMETRY_ENABLED: false

      # SMTP for invites / password resets
      PENPOT_SMTP_HOST: smtp.example.com
      PENPOT_SMTP_PORT: 587
      PENPOT_SMTP_DEFAULT_FROM: penpot@example.com
      PENPOT_SMTP_DEFAULT_REPLY_TO: penpot@example.com
      PENPOT_SMTP_USERNAME: penpot@example.com
      PENPOT_SMTP_PASSWORD: ${SMTP_PW}
      PENPOT_SMTP_TLS: true

  penpot-exporter:
    image: penpotapp/exporter:latest
    restart: unless-stopped
    environment:
      PENPOT_PUBLIC_URI: http://penpot-frontend

  penpot-postgres:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      POSTGRES_USER: penpot
      POSTGRES_DB: penpot
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - penpot-postgres:/var/lib/postgresql/data

  penpot-redis:
    image: redis:7-alpine
    restart: unless-stopped

volumes:
  penpot-postgres:
  penpot-assets:
docker compose up -d
docker compose logs -f penpot-backend

Reverse proxy

# Caddy
design.example.com {
    reverse_proxy 127.0.0.1:9001
    request_body { max_size 100MB }
}

WebSockets for collaboration; Caddy handles them automatically.

First-run setup

Browse to https://design.example.com. Register the first user (will be the team admin). Penpot prompts to create a team; everything below is per-team.

After bootstrap, set PENPOT_FLAGS to drop enable-registration (so the instance stops accepting new public signups) and require invites instead.

What Penpot can do

  • Vector editor — rectangles, ellipses, paths, text, boolean ops, shape morphing. SVG-native primitives, so every shape is a real path you could inspect.
  • Components — reusable design elements with overrides. Edit the master, instances update. Variants for component states.
  • Auto-layout — flex-style automatic spacing and resizing.
  • Libraries — share components, color styles, typography across files; manage as design systems.
  • Design tokens — W3C-spec-compliant tokens for colors, spacing, typography. Exportable as JSON for engineering consumption.
  • Prototype — flows, interactions, transitions, scrolling, hover states. Shareable preview links.
  • Real-time collaboration — multiple users editing the same file with live cursors.
  • Inspector — developer hand-off view showing CSS, sizes, colors, assets.
  • Export — SVG (the native format), PNG, JPG, PDF, individual layers or entire boards.

Plugins (2.x+)

Penpot 2.x added a plugin API. Plugins run sandboxed in the user's browser, can read / modify files via the public API. Notable picks:

  • HTML/CSS export — turn a board into a hand-coded HTML + CSS snippet.
  • Lorem Ipsum / placeholder data — fill in mockups.
  • Color palette extractors — from images.
  • Design token import/export — sync with code repos.
  • Translation — bulk-translate text layers.

Install via the Plugins panel in any file; per-team plugin policy in Team Settings → Plugins.

Import from Figma

Penpot's importer reads .fig files (export your Figma file as .fig via the desktop Figma app or paid plan). Most layers translate; component variants, advanced auto-layout, and certain prototype interactions are imperfect. For one-off file migrations, it's good enough; for active project migration, plan to re-create complex components.

Penpot's own file format is .penpot (a zip of JSON + binary assets); the API also supports raw import/export of project trees.

SSO

Penpot supports OIDC out of the box — Authentik (see that tutorial), Keycloak, Google, GitHub. Set the OIDC env vars and disable password auth for SSO-only flows:

PENPOT_FLAGS: disable-registration enable-login-with-oidc enable-oidc-registration
PENPOT_OIDC_CLIENT_ID: <client-id>
PENPOT_OIDC_CLIENT_SECRET: <client-secret>
PENPOT_OIDC_BASE_URI: https://auth.example.com/application/o/penpot/
PENPOT_OIDC_SCOPES: "openid profile email"
PENPOT_OIDC_NAME: "Authentik"

Object storage for big projects

For large teams, swap the filesystem backend for S3-compatible storage (MinIO; see that tutorial):

PENPOT_ASSETS_STORAGE_BACKEND: assets-s3
PENPOT_STORAGE_ASSETS_S3_REGION: us-east-1
PENPOT_STORAGE_ASSETS_S3_BUCKET: penpot-assets
PENPOT_STORAGE_ASSETS_S3_ENDPOINT: https://s3.example.com
AWS_ACCESS_KEY_ID: <key>
AWS_SECRET_ACCESS_KEY: <secret>

Files and assets stay in S3; Postgres holds metadata only.

Backups

  • Postgres — the project / team / file structure, comments, versions. pg_dump nightly.
  • Assets storage — the actual file blobs. Either restic on the local volume or snapshots / replication on the S3 bucket.
  • Penpot can also export a project tree via API as a single archive — useful for per-team backups.

Penpot vs Figma in 2026

  • Penpot is materially less polished on the most-advanced features (some complex auto-layout, certain prototype interactions, the multi-file design-system experience). Improving every release.
  • Performance on very-large files is workable but slower than Figma's native client.
  • For self-hosted requirement, source availability, or "we want to own our design files in an open format," Penpot is the only credible option.
  • Roadmap and core team are funded and active (Kaleidos, a Spanish company); not a project at risk of abandonment.

For sovereignty over design files, regulated industries where SaaS isn't permitted, or simply not paying per-seat for design tools, Penpot is the answer.