Why Penpot Is Worth Running on Your Own Server
Figma’s shift to subscription pricing – and its cloud-only architecture – left a lot of design teams quietly reconsidering their options. Penpot fills that gap directly: it’s an open-source design and prototyping tool built on web standards, runs entirely in a browser, and can be self-hosted on hardware you control. No usage caps, no seat licensing headaches, no data leaving your infrastructure.
The practical appeal goes beyond cost. Teams working in regulated industries or under strict data governance policies often can’t send design files through third-party cloud services. Self-hosting Penpot puts that decision back in your hands. The setup process is approachable if you’re comfortable with Docker, and the official image is well-maintained. This guide walks through a full deployment from scratch.

What You Need Before You Start
Penpot is distributed as a multi-container application, so Docker and Docker Compose are the baseline requirements. A Linux server – whether a VPS, a home lab machine, or a dedicated box – with at least 4GB of RAM and 2 CPU cores will handle small to mid-sized teams without strain. Larger teams doing heavy file work will want to scale up from there. You’ll also need a domain name pointed at your server if you plan to expose Penpot over HTTPS, which you should.
Open ports 80 and 443 on your firewall before starting. If you’re running ufw, the commands are straightforward: sudo ufw allow 80 and sudo ufw allow 443. Have your DNS record pointing to your server’s IP address before you attempt to generate an SSL certificate – Certbot will verify domain ownership over port 80, and a failed check can trigger rate limits on Let’s Encrypt’s end.

Installing and Configuring Penpot with Docker Compose
The official Penpot team maintains a Docker Compose file on GitHub that includes all the services you need: the frontend, backend, exporter, and a PostgreSQL database. Download it directly with curl -o docker-compose.yaml https://raw.githubusercontent.com/penpot/penpot/main/docker/images/docker-compose.yaml. This pulls the latest stable configuration without requiring you to clone the entire repository.
Before running anything, open the Compose file and review the environment variables in the penpot-backend service block. The two you must set are PENPOT_PUBLIC_URI and the SMTP configuration if you want email-based registration and password recovery. Set PENPOT_PUBLIC_URI to your full domain, including the protocol – for example, https://design.yourdomain.com. Without this, internal links in notification emails and shared file URLs will break.
For SMTP, Penpot accepts standard environment variables: PENPOT_SMTP_HOST, PENPOT_SMTP_PORT, PENPOT_SMTP_USERNAME, and PENPOT_SMTP_PASSWORD. If you’re running a transactional email service like Postmark or Mailgun, drop in those credentials here. If email isn’t a priority and you just want to test, set PENPOT_FLAGS to include enable-login-with-password and disable-email-verification – this lets you create accounts without confirming an inbox first.
Once your environment variables are set, bring the stack up with docker compose up -d. The first run pulls several images, so expect a few minutes depending on your connection speed. When it completes, all four containers should show a status of Up when you run docker compose ps. The application won’t be accessible over HTTPS yet – that comes in the next step.
Setting Up a Reverse Proxy with HTTPS
Penpot’s frontend listens on port 3449 internally, and the backend API runs on port 6060. You don’t expose these directly – instead, you route traffic through a reverse proxy that handles SSL termination. Nginx is the standard choice here and pairs well with Certbot for automated certificate management.
Install both on Ubuntu or Debian with sudo apt install nginx certbot python3-certbot-nginx. Create a new server block in /etc/nginx/sites-available/penpot. The minimal configuration you need passes all requests to the Penpot frontend and correctly proxies WebSocket connections, which Penpot uses for real-time collaboration. Set the proxy_pass directive to http://localhost:3449, and add the standard WebSocket headers: Upgrade and Connection. Without those headers, the collaborative cursor and live-update features will silently fail.
Run sudo certbot –nginx -d design.yourdomain.com to obtain a certificate and let Certbot modify your Nginx config automatically. It will add the SSL directives, redirect HTTP to HTTPS, and configure certificate renewal. After that completes, reload Nginx with sudo systemctl reload nginx and navigate to your domain in a browser. The Penpot login screen should load over a valid HTTPS connection.

Creating Your First Admin Account and Team
Penpot doesn’t ship with a default admin credential. Instead, you create the first user through the registration flow on the login page. If you disabled email verification during setup, the account activates immediately. That first registered account has no special admin privileges in the traditional sense – Penpot’s permission model is project and team-based rather than role-based at the system level.
Once you’re logged in, create a team from the left sidebar and invite collaborators by email. Each invitee receives a registration link, signs up, and lands directly in your shared workspace. Penpot’s permission tiers are simple: owners, editors, and viewers. Files and projects live under the team, so access control is set at the team level rather than on individual files. If you’re running Penpot for a client team where you want them to view but not edit, the viewer role does exactly that without additional configuration.
One thing to do early: enable the enable-webhooks flag in your backend environment variables if you want Penpot to push events to external services. It’s off by default. Penpot also supports a flags system that controls feature availability across the installation – worth reviewing the full flags list in the documentation before you go live, since some features like the access tokens API require explicit activation. Turning on the wrong flag after users have started working doesn’t cause data loss, but it can expose incomplete features to your team before they’re production-ready.





