Deployment guide

Self-host Penpot: open-source Figma alternative on your VPS

Deploy on a VPS Cloud →

Tutorial

Development

Self-host Penpot: open-source Figma alternative on your VPS

Figma has become the standard for interface design, but it remains a proprietary service: your files live with a third party, billed per seat. Penpot (MPL-2.0 licence) is the open-source alternative: a real-time collaborative design tool built on web standards (SVG, CSS) that you self-host with Docker. This guide goes from installation on a ServOrbit VPS to day-to-day operation: sized requirements, email, backup, updates, memory limits, importing from Figma, and troubleshooting with the real error messages.

12 min read8 steps
Contents· Penpot vs Figma: what the open-source alternative really changes1/14
  1. 01Penpot vs Figma: what the open-source alternative really changes
  2. 02What Penpot offers out of the box
  3. 03Architecture: one Docker stack, a single HTTP entry point
  4. 04The services of the Penpot stack
  5. 05Sized requirements: RAM, CPU, disk, ports
  6. 06Deployment in 8 steps
  7. 07Post-install configuration: email and file storage
  8. 08Backup and updates
  9. 09Memory: what issue #3299 reports and how to cap it
  10. 10Sizing memory: limit, monitoring, scheduled restart
  11. 11Importing your files from Figma
  12. 12Fitting Penpot into your Git workflow
  13. 13Penpot MCP: drive your designs from an AI assistant
  14. 14Troubleshooting: the errors actually reported

Penpot vs Figma: what the open-source alternative really changes

Figma and Penpot share the same paradigm: vector design in the browser, real-time collaboration, reusable components and built-in developer handoff. The fundamental difference lies in the data model and in control. Figma stores everything on its own servers, in a proprietary format. Penpot builds on SVG and CSS as its base language and runs on your infrastructure: you decide where the files live, who connects and when you update. For a team that already self-hosts its code on Gitea, its documentation on Docmost and its chat on Mattermost, hosting the design tool on ServOrbit as well is the logical next step. The price is operations: backups, updates and sizing become your job, which is what the following sections cover.

What Penpot offers out of the box

  • Real-time vector design — several designers on the same file at once, visible cursors, contextual comments on layers.
  • Components and shared libraries — build design systems and publish them as libraries shared across projects.
  • Native developer handoff — CSS inspector, asset export, annotations and spacing measurements generated from the designs.
  • Interactive prototyping — link boards with transitions and simulate navigation without leaving the tool.
  • Flexible layouts — auto layouts, CSS grids and resize constraints comparable to those in Figma.
  • Open formats — .penpot export (full archive) and per-board SVG export, readable in Inkscape or any vector editor.
  • MCP server — the penpot-mcp service exposes your files to MCP-compatible assistants (see the dedicated callout below).

Architecture: one Docker stack, a single HTTP entry point

Penpot is a stack of several containers. The Compose file published by the project declares, at version 2.18, eight services: penpot-frontend, penpot-backend, penpot-admin-console, penpot-mcp, penpot-exporter, penpot-postgres, penpot-valkey and penpot-mailpit. The ServOrbit template starts a subset depending on its version: read the exact number with docker compose ps on your VPS. The table below gives the role of each service as the upstream Compose file describes it. Only one port matters for exposure: the frontend listens internally on 8080 and the Compose file publishes it on 9001. Put your reverse proxy in front of that port and nothing else.

The services of the Penpot stack

Scroll the table

ServiceRoleGood to know
`penpot-frontend`Serves the application and routes requests to the other servicesThe only service to expose: port 9001 in the upstream Compose file
`penpot-backend`API and business logic (Java/Clojure application)Files (assets) in a volume; SMTP and storage are configured here
`penpot-exporter`PNG, PDF and SVG exports through a headless browserReaches the app through `PENPOT_PUBLIC_URI`: a valid certificate is required
`penpot-postgres`PostgreSQL 15 database: accounts, projects, filesVolume `penpot_postgres_v15` must be backed up
`penpot-valkey`Real-time notifications (websockets) and cacheCapped at 128 MB (`--maxmemory 128mb`) in the upstream Compose file
`penpot-mcp`MCP server for AI assistantsEnabled by the `enable-mcp` flag, websocket `/mcp/ws`
`penpot-admin-console`Administration consoleAdded to the stack in 2.18, flag `enable-admin-console`
`penpot-mailpit`Test SMTP inbox (interface on port 8025)Replace with a real SMTP server in production

Sized requirements: RAM, CPU, disk, ports

The upstream documentation publishes no minimum floor for a small team. It states that 4 CPUs and 16 GB of RAM are enough to serve thousands of users, and lets you be conservative when allocating resources. For disk, it recommends starting from 50 to 100 GB of database space with elastic sizing, which covers up to 10 editors, then roughly 5 GB more per additional editor. The real size depends on usage and file complexity, not on the number of accounts. Our editorial recommendation for a team of a few people: a VPS with 4 GB of RAM and a limit per service (memory section below), which leaves headroom for exporting heavy files; 2 GB is fine for a trial. On the network side, open 80 and 443 to the reverse proxy, never 9001 or 8025 directly. The maximum request body in the Compose file is 367001600 bytes (350 MiB): the client_max_body_size directive of your proxy must follow. On the workstation side, the documentation recommends the latest stable version of Chrome.

Deployment in 8 steps

  1. Order a ServOrbit VPS

    Plan for 4 GB of RAM for team use, 2 GB for a trial. Ubuntu 24.04 LTS is the default template. Keep at least 50 GB of free disk if you import large files.

  2. Point a subdomain at your VPS

    Create an A record for design.your-domain.com pointing to the VPS IP. The ServOrbit provisioner issues a Let's Encrypt certificate. An HTTPS domain is essential: Penpot sets secure session cookies by default, and the upstream documentation reserves disable-secure-session-cookies for non-production trials.

  3. Install from the marketplace

    In the ServOrbit console: Marketplace, Collaboration, Penpot, Deploy. The AWX job starts the containers, generates PENPOT_SECRET_KEY and sets PENPOT_PUBLIC_URI to your domain. The interface answers on https://design.your-domain.com within a few minutes.

  4. Check that every service is running

    Connect over SSH and run docker compose ls to locate the Compose file, then docker compose -p penpot -f docker-compose.yaml ps. Each service should be running, with penpot-postgres and penpot-valkey reported healthy. Logs are read with docker compose -p penpot -f docker-compose.yaml logs -f.

  5. Create your account

    Registration is open by default. Open https://design.your-domain.com and click "Create an account": in the upstream Compose file email verification is disabled (disable-email-verification), so the account is active immediately. Create yours right away, before anyone else does.

  6. Close public registration

    Add the disable-registration flag to PENPOT_FLAGS (frontend and backend) and recreate the containers. Invitations keep working as long as enable-login-with-password stays active. To create an account without an invitation, run docker exec -ti penpot-penpot-backend-1 python3 manage.py create-profile: the script requires the enable-prepl-server flag and the container name may vary, check it with docker ps.

  7. Connect a real SMTP server

    Without SMTP, no invitation or password reset email is sent. See the next section for the variables.

  8. Import your Figma files

    Penpot does not open .fig files directly. The official route goes through the "Penpot Exporter" Figma plugin, which produces a .zip archive to import into Penpot. The procedure and its limits are detailed below.

Post-install configuration: email and file storage

For email, enable enable-smtp in PENPOT_FLAGS and set on the backend PENPOT_SMTP_DEFAULT_FROM, PENPOT_SMTP_DEFAULT_REPLY_TO, PENPOT_SMTP_HOST, PENPOT_SMTP_PORT (587 in the upstream example), PENPOT_SMTP_USERNAME, PENPOT_SMTP_PASSWORD and PENPOT_SMTP_TLS. The upstream Compose file points to penpot-mailpit by default, which keeps messages in memory and loses them when the container is recreated. In production, also remove disable-email-verification, which the documentation advises against outside trials. For files, the images and SVG clips uploaded by users are stored on the file system by default, in the penpot_assets volume mounted at /opt/data/assets (PENPOT_OBJECTS_STORAGE_BACKEND: fs). S3-compatible storage is possible with PENPOT_OBJECTS_STORAGE_BACKEND: s3, a bucket, an optional endpoint and the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY variables. Since version 2.11.0 the old PENPOT_STORAGE_ASSETS_* names have been replaced by PENPOT_OBJECTS_STORAGE_*: if you reuse an old Compose file, rename them.

Backup and updates

A Penpot instance has two pieces of state to back up together: the PostgreSQL database and the files volume. The upstream documentation warns that you cannot directly copy the data folder of a Docker volume, and points to Docker's volume backup procedure. Our approach: a logical export of the database with docker exec penpot-penpot-postgres-1 pg_dump -U penpot penpot > penpot.sql, then an archive of the files volume through a temporary container that mounts it read-only. Find the volume names with docker volume ls. Take both at the same moment, otherwise the database may reference files missing from the archive, and test a restore on another VPS before you need it. For updates, pin the version with PENPOT_VERSION rather than following the latest image, run docker compose -p penpot -f docker-compose.yaml pull then up -d, and move in small steps: the documentation advises against jumping between distant versions. As of 1 November 2026, the latest published version is 2.18.5. If you maintain your own Compose file, 2.18 added the penpot-admin-console service and the PENPOT_ADMIN_CONSOLE_URI variable on the frontend and the backend. Always back up before updating.

Memory: what issue #3299 reports and how to cap it

Penpot issue #3299, opened in June 2023, is still open and received a message in October 2026. It gathers reports of high RAM use on Docker installations: one user reports about 20 GB used after three to four days on a 64 GB server, another more than 50 GB requested by a VM with no users, another 15 GB for a single user with the backend restarted every 12 hours. These are individual reports, not reproduced: a member of the Penpot team answers in the thread that they run high-traffic Docker installations without this symptom, and asks for a file and traces to diagnose. We therefore cannot promise a consumption figure. What we can say is how to avoid letting one service decide alone: the backend is a Java process, and without a container limit the Java virtual machine sizes its heap from the memory it sees, that is, the memory of the whole host. A per-service limit turns a silent drift into a controlled restart.

Sizing memory: limit, monitoring, scheduled restart

A contributor to thread #3299 reports adding mem_limit: 12g to every service of the Compose file to keep consumption bearable. On a 4 GB VPS, set lower values, for example mem_limit: 2g on penpot-backend and mem_limit: 1g on penpot-exporter, and leave the rest to PostgreSQL and the system. The backend launch script appends JVM_OPTS and JAVA_OPTS to the Java command line: you can pass -XX:MaxRAMPercentage=70 there, a standard JVM option, but the Penpot documentation describes no JVM tuning, so test it before production. Monitor with docker stats --no-stream, and if the backend drifts despite the limit, plan a nightly restart (docker compose -p penpot -f docker-compose.yaml restart penpot-backend): collaborators reconnect their websockets. Valkey is already capped at 128 MB in the upstream Compose file. If the limit is hit too often, that is the signal to choose a VPS plan with more RAM, not to raise the limit blindly.

Importing your files from Figma

Contrary to what you sometimes read, Penpot does not import a .fig file. The project maintains a Figma plugin, "Penpot Exporter", which converts a Figma file into an importable .zip archive. In Figma, open the file, then Resources, Plugins, search for "Penpot Exporter" and run the export. In Penpot, open the three-dot menu of a project or of drafts, choose "Import Penpot files" and select the .zip. According to the plugin README, these are carried over: basic shapes, vectors, frames and sections (boards in Penpot), groups and masks, texts, fills, strokes, shadows, components and instances, auto layouts, variants and colour and typography libraries. Not carried over: prototyping settings. Very large files remain a hard point, because the export walks every node under the Figma API limits, and some renderings may differ slightly. Import a test file first, compare components and fonts, then migrate the rest. Custom fonts are uploaded to Penpot.

Fitting Penpot into your Git workflow

Penpot does not store your designs as text files in a repository: the source of truth is the PostgreSQL database. Two exports, however, suit Git. The .penpot archive serves as a portable backup of a file. The SVG export of a board produces readable text that you can commit to Gitea, for example for the icons and illustrations of a design system. A pull request then shows the SVG diff, and your CI pipeline can detect an unexpected change. This approach remains a team convention: it assumes re-exporting after each change, which a script or a design review can remind you to do.

Penpot MCP: drive your designs from an AI assistant

The upstream Compose file includes a penpot-mcp service, enabled by the enable-mcp flag, and its reverse proxy must relay the /mcp/ws websocket in addition to /ws/notifications. An MCP-compatible client can then connect to your instance. Reserve this access for trusted accounts and serve it over HTTPS. Test on a draft file first, then generalise if the result suits you.

Troubleshooting: the errors actually reported

  • "Something wrong has happened" banner or rejected auth-token cookie — reported on a server reached over HTTP from the local network. Answer from a Penpot team member: cookies are secure by default, so frontend and backend must be served over HTTPS. Serve the instance behind a valid certificate and set PENPOT_PUBLIC_URI to the exact URL.
  • Internal error after a few clicks on sign-up or login — reported with disable-secure-session-cookies on public HTTP access. Same remedy: HTTPS. The flag is not a production fix.
  • WebSocket connection to 'wss://…/ws/notifications…' failed: Invalid frame header — reported right after login, with the interface freezing, behind Nginx. The answer given in the community is to relay /ws/notifications with the Upgrade and Connection headers to port 9001. The thread does not confirm that this resolved the requester's case.
  • net::ERR_CERT_AUTHORITY_INVALID in the penpot-exporter logs — the export fails. The exporter loads the application through PENPOT_PUBLIC_URI: with a self-signed or unrecognised certificate, it cannot validate the connection. Use a certificate from a recognised authority.
  • PDF export that times out without a clear message — reported on a manual installation where the exporter received PENPOT_PUBLIC_URI=http://localhost. The exporter must be able to reach the public URL of the application, not an address local to its own container.
  • Cannot log in, no lead — first check docker compose -p penpot -f docker-compose.yaml ps, then the logs of the service concerned, and the browser console (F12), which shows the exact Penpot version and the failing requests.

Your design studio under your control

Deploy Penpot on a ServOrbit VPS and give your team a sovereign collaborative design space — no per-seat subscription, no design data at Adobe.