Self-Hosting Penpot on Docker Swarm: A Guide to High-Availability Collaborative Design Production
Introduction to Enterprise-Grade Self-Hosted Design Ecosystems
In the modern digital product development lifecycle, user interface (UI) and user experience (UX) design tools have become critical infrastructure. For organizations prioritizing data sovereignty, security compliance, and cost efficiency, relying solely on proprietary SaaS solutions poses significant long-term risks. Enter Penpot, the first open-source, web-based design and prototyping platform that leverages native SVG files. By eliminating vendor lock-in, Penpot empowers cross-functional teams of designers and developers to collaborate seamlessly.
However, running a design platform at an enterprise level demands more than a basic single-container deployment. To support concurrent users, intensive rendering workloads, and continuous uptime requirements, deploying Penpot on a distributed infrastructure is paramount. This guide provides a comprehensive technical blueprint for self-hosting Penpot on a Docker Swarm cluster, transforming it into a resilient, High-Availability (HA) production environment.
Understanding the Penpot Architecture
Before initiating the deployment process, it is essential to understand the decoupled nature of Penpot’s microservices. A standard production deployment consists of several interconnected components:
- Penpot Frontend: A highly optimized Nginx server that delivers the compiled static assets (HTML, JavaScript, CSS, and SVG components) to the client browser.
- Penpot Backend (Core): A Clojure-based application server responsible for processing business logic, handling HTTP API traffic, and managing real-time WebSocket connections for multi-user collaboration.
- Penpot Exporter: A specialized Node.js service running a headless browser instance dedicated to rendering and exporting design files to PDF, PNG, or JPEG formats.
- Database Layer (PostgreSQL): The relational database acting as the single source of truth for user profiles, project metadata, access control lists, and workspace hierarchies.
- Cache Layer (Redis): An in-memory data store utilized for session tracking, WebSocket message brokering, and transient background job queues.
Deploying this stack across a Docker Swarm cluster allows individual services to scale independently based on utilization patterns, such as scaling the Exporter service during heavy asset generation or the Backend service during peak collaboration hours.
Prerequisites and Infrastructure Planning
To establish a truly resilient topology, your underlying infrastructure must eliminate single points of failure (SPOFs). Ensure your environment aligns with the following minimum specifications:
- Compute Cluster: A minimum of three virtual or physical machines configured as a Docker Swarm cluster (one Primary Manager node and two Worker nodes to maintain quorum and handle scheduling failovers).
- Operating System: Linux-based distributions (e.g., Ubuntu Server 22.04 LTS or Rocky Linux 9) with Docker Engine CE and the Swarm mode initialized.
- Shared Storage Solution: A distributed file system such as GlusterFS, Ceph, or a highly available Network File System (NFS) mount across all cluster nodes to provide persistent volume access for PostgreSQL, Redis, and asset storage.
- Network Routing: A reverse proxy or external load balancer (e.g., HAProxy, Traefik, or an AWS Application Load Balancer) situated in front of the Swarm ingress network to distribute ingress traffic.
Step 1: Orchestrating High-Availability Storage
Docker Swarm manages container scheduling dynamically across multiple nodes. Because data must persist regardless of which node executes a task, static local host volumes are inadequate. We must leverage a replicated volume plugin or a shared directory path.
Crucial Note: PostgreSQL requires POSIX-compliant locking mechanisms. If utilizing an NFS share, ensure it is configured with theno_root_squashandrwoptions, or optimally, utilize a dedicated cloud-managed database instance or Patroni-managed PG cluster for the database layer.
Assuming a shared directory is mounted at /mnt/shared/penpot across all swarm nodes, create the necessary directory structure:
sudo mkdir -p /mnt/shared/penpot/postgres
sudo mkdir -p /mnt/shared/penpot/assets
sudo mkdir -p /mnt/shared/penpot/redis
Step 2: Crafting the Docker Swarm Stack Definition
Below is the declarative docker-compose.yml deployment file optimized for Docker Swarm routing, service replication, and health monitoring. This configuration utilizes Swarm secrets and environment variables to manage sensitive credentials securely.
version: '3.8'
services:
penpot-frontend:
image: penpotapp/frontend:latest
networks:
- penpot-net
ports:
- "8080:80"
deploy:
replicas: 2
update_config:
parallelism: 1
delay: 10s
restart_policy:
condition: on-failure
penpot-backend:
image: penpotapp/backend:latest
volumes:
- /mnt/shared/penpot/assets:/opt/data
environment:
- PENPOT_DATABASE_URI=postgresql://penpot_user:SecurePassword123@penpot-postgres:5432/penpot_db
- PENPOT_REDIS_URI=redis://penpot-redis:6379/0
- PENPOT_ASSETS_STORAGE_BACKEND=assets-fs
- PENPOT_STORAGE_ASSETS_FS_DIRECTORY=/opt/data
- PENPOT_FLAGS=enable-registration enable-login-with-password
networks:
- penpot-net
deploy:
replicas: 3
placement:
max_replicas_per_node: 1
restart_policy:
condition: on-failure
penpot-exporter:
image: penpotapp/exporter:latest
environment:
- PENPOT_PUBLIC_URI=http://penpot-frontend
networks:
- penpot-net
deploy:
replicas: 2
resources:
limits:
cpus: '1.0'
memory: 1G
penpot-postgres:
image: postgres:15-alpine
volumes:
- /mnt/shared/penpot/postgres:/var/lib/postgresql/data
environment:
- POSTGRES_USER=penpot_user
- POSTGRES_PASSWORD=SecurePassword123
- POSTGRES_DB=penpot_db
networks:
- penpot-net
deploy:
placement:
constraints:
- node.role == manager
penpot-redis:
image: redis:7-alpine
volumes:
- /mnt/shared/penpot/redis:/data
networks:
- penpot-net
deploy:
placement:
constraints:
- node.role == manager
networks:
penpot-net:
driver: overlay
attachable: true
Step 3: Deploying and Validating the Swarm Stack
With the stack file configured, execute the deployment command from your Swarm Manager node. This initializes the overlay network and orchestrates the creation of the services across the cluster:
docker stack deploy -c docker-compose.yml penpot_prod
To verify the state of your high-availability deployment and confirm that the container tasks have distributed evenly across the manager and worker nodes, run the following status command:
docker stack ps penpot_prod
The Swarm routing mesh will automatically handle incoming requests on port 8080 and distribute them across your active penpot-frontend replicas, ensuring seamless user experiences even during node maintenance operations.
Step 4: Implementing Advanced Network Security and TLS
Exposing database credentials over standard HTTP ports is not acceptable for enterprise workflows. It is highly recommended to place an edge reverse proxy like Traefik or Nginx Proxy Manager within the Swarm configuration to handle SSL/TLS termination, automated Let’s Encrypt certificate renewals, and WebSocket connection upgrades.
Ensure that your proxy configuration increases the maximum allowed request body size (e.g., client_max_body_size 256M;) to support large SVG asset and design file uploads without triggering HTTP 413 Payload Too Large errors.
Conclusion and Day-Two Operations
By self-hosting Penpot on Docker Swarm, your organization achieves total control over its creative property while benefiting from automated container self-healing, scaling, and zero-downtime updates. To sustain this architecture over time, remember to establish automated database dump routines for PostgreSQL and monitor cluster resource metrics utilizing tools like Prometheus and Grafana. Your design team can now collaborate on a modern, open platform with the performance and reliability guarantees mandated by enterprise operations.
