Upstream docs: https://help.penpot.app/
Everything not listed in this document should behave the same as upstream Penpot 2.15.3. If a feature, setting, or behavior is not mentioned here, the upstream documentation is accurate and fully applicable.
Penpot is the web-based, open-source design and prototyping tool for teams. This package bundles the full Penpot stack — frontend, backend, exporter, and the required PostgreSQL and Valkey datastores — into a single self-contained StartOS service with no external dependencies.
- Image and Container Runtime
- Volume and Data Layout
- Installation and First-Run Flow
- Configuration Management
- Network Access and Interfaces
- Actions
- Backups and Restore
- Health Checks
- Limitations and Differences
- What Is Unchanged from Upstream
- Contributing
All images are upstream, unmodified, and pinned to the same version. Each runs in its own subcontainer using the image's default entrypoint:
| Component | Image | Role |
|---|---|---|
frontend |
penpotapp/frontend:2.15.3 |
nginx; serves the SPA and proxies API/exporter |
backend |
penpotapp/backend:2.15.3 |
Clojure API server, runs DB migrations |
exporter |
penpotapp/exporter:2.15.3 |
Renders exports (PDF/PNG/SVG) |
postgres |
postgres:15-alpine |
Bundled database |
valkey |
valkey/valkey:8.1-alpine |
Bundled Redis-compatible cache / pub-sub |
- Architectures:
x86_64,aarch64. - All subcontainers share the package network namespace, so every component
reaches the others over
127.0.0.1. The frontend's nginx upstreams are pointed at127.0.0.1viaPENPOT_BACKEND_URI/PENPOT_EXPORTER_URI(using IPs instead of hostnames avoids the image's DNS-resolver requirement).
Single main volume, with these subpaths:
| Subpath | Mounted at | Contents |
|---|---|---|
postgres |
/var/lib/postgresql/data |
PostgreSQL data (PGDATA=.../pgdata) |
assets |
/opt/data/assets |
Uploaded objects (fs storage backend) |
store.json |
— | StartOS-managed secrets (see below) |
store.json holds two values generated once on install:
secretKey→PENPOT_SECRET_KEY(shared by backend and exporter)dbPassword→ the bundled PostgreSQLpenpotrole password
Differs from upstream's docker-compose example in these ways:
- No external services to wire up. PostgreSQL and Valkey are bundled and auto-configured; their credentials are generated on install.
- Object storage is local filesystem (
PENPOT_OBJECTS_STORAGE_BACKEND=fs), stored on themainvolume. - No setup wizard and no pre-created admin. Open the Web UI and register the first account via Penpot's normal sign-up screen — that account is yours.
| StartOS-Managed (fixed by this package) | Upstream-Managed (inside Penpot) |
|---|---|
PENPOT_SECRET_KEY, PENPOT_DATABASE_*, PENPOT_REDIS_URI |
Account/profile settings |
PENPOT_PUBLIC_URI (backend/exporter only; see below) |
Teams, projects, files, libraries |
PENPOT_OBJECTS_STORAGE_BACKEND=fs, PENPOT_OBJECTS_STORAGE_FS_DIRECTORY |
Fonts, plugins, sharing/permissions |
PENPOT_BACKEND_URI, PENPOT_EXPORTER_URI (internal wiring) |
|
PENPOT_FLAGS (see below), PENPOT_TELEMETRY_ENABLED=false |
PENPOT_PUBLIC_URI is intentionally not set on the frontend. With it
unset, config.js omits penpotPublicURI and the SPA falls back to
location.origin, so API/websocket/worker URLs track whatever origin the user
is on — .local, .onion, LAN IP, or a custom domain all work simultaneously.
It is still set on the backend and exporter (for server-side link generation
and internal page rendering).
PENPOT_FLAGS is set to:
disable-email-verification disable-secure-session-cookies disable-telemetry
disable-email-verification— no mail server ships, so accounts are usable immediately on registration.disable-secure-session-cookies— StartOS serves the UI over plain HTTP on LAN/Tor, so cookies must not require theSecureattribute.disable-telemetry— no usage data is sent upstream.
| Interface | Internal port | Protocol | Purpose |
|---|---|---|---|
ui |
8080 | HTTP | Penpot web application |
Reachable via LAN IP, .local address, .onion (Tor), and any custom domain
configured in StartOS. The backend (6060), exporter (6061), PostgreSQL (5432),
and Valkey (6379) are internal only and not exposed.
None. The first account is created through Penpot's own registration screen.
sdk.Backups.ofVolumes('main') — backs up the entire main volume: the
PostgreSQL data directory, uploaded assets, and store.json. Restoring brings
back all designs, accounts, and the original secret key / DB password together,
so the database remains readable.
Each component exposes a port-listening check; ordering is enforced via
requires:
| Daemon | Port | Grace period | Requires |
|---|---|---|---|
| postgres | 5432 | default | — |
| valkey | 6379 | default | — |
| backend | 6060 | 180s | postgres, valkey |
| exporter | 6061 | 60s | valkey |
| frontend | 8080 | 60s | backend, exporter |
The backend's longer grace period covers first-boot database migrations.
- No email delivery. Password-reset and email-invite flows will not send mail (no SMTP configured). Registration still works (verification disabled).
- Server-generated absolute links use one address. The frontend tracks the
browser origin (see
PENPOT_PUBLIC_URIabove), so the UI works over any origin. However, the backend'sPENPOT_PUBLIC_URIis a single StartOS address, so any absolute URL Penpot generates server-side (e.g. in email) points at that one address. With no SMTP configured this is rarely hit. - Filesystem object storage only. S3 backends are not configured.
- MCP / mailcatcher components from the upstream compose are omitted.
- No bundled admin account — the first registered user is the de-facto owner.
- Harmless
css/ui.css404 in the browser console. Upstream'sindex.htmlreferencescss/ui.css, which the official frontend image does not ship (all styling lives incss/main.css). The request 404s in every Penpot deployment; it has no functional effect.
- The Penpot application itself — design tools, prototyping, components, libraries, plugins, export, sharing, and the SVG-based file format.
- All Penpot images are upstream and unmodified, using their default entrypoints.
- PostgreSQL and Valkey behave as standard
postgres:15/valkey:8.1.
See CONTRIBUTING.md for build instructions.
package_id: penpot
upstream_version: 2.15.3
images:
frontend: penpotapp/frontend:2.15.3
backend: penpotapp/backend:2.15.3
exporter: penpotapp/exporter:2.15.3
postgres: postgres:15-alpine
valkey: valkey/valkey:8.1-alpine
architectures: [x86_64, aarch64]
volumes:
main:
postgres: /var/lib/postgresql/data
assets: /opt/data/assets
store.json: StartOS-managed secrets
ports:
ui: 8080
internal_ports:
backend: 6060
exporter: 6061
postgres: 5432
valkey: 6379
dependencies: none
startos_managed_env_vars:
- PENPOT_SECRET_KEY
- PENPOT_PUBLIC_URI
- PENPOT_DATABASE_URI
- PENPOT_DATABASE_USERNAME
- PENPOT_DATABASE_PASSWORD
- PENPOT_REDIS_URI
- PENPOT_OBJECTS_STORAGE_BACKEND
- PENPOT_OBJECTS_STORAGE_FS_DIRECTORY
- PENPOT_BACKEND_URI
- PENPOT_EXPORTER_URI
- PENPOT_FLAGS
- PENPOT_TELEMETRY_ENABLED
actions: []