You are here: Home / Unofficial Penpot Manual Installation Guide for Debian 12

Unofficial Penpot Manual Installation Guide for Debian 12

Penpot opensource free self-hostable server (Figma clone) does not support manual install. They want everyone to use their SaaS cloud service or if self-hosting, use containerized versions. Here are instructions, from (as they suggested) reverse engineering their Docker setup, for a manual setup on Debian 12. YMMV.

Penpot Manual Installation Guide for Debian 12

This guide attempts to walk through setting up a self-hosted Penpot instance manually on Debian 12, reverse-engineered from the official Docker configuration. This approach aims to give you full control over the installation process and a deeper understanding of Penpot's architecture.

Architecture Overview

Penpot has the architecture of a typical single page application (SPA). There is a frontend application, written in ClojureScript and using React framework, and served from a static web server. It talks to a backend application, that maintains data on a PostgreSQL database. The backend is written in Clojure, so the frontend and backend can share code and data. Then, the code is compiled into JVM bytecode and run in a JVM environment.

The core components you'll need to install and configure are:

  1. PostgreSQL 15 - Primary database
  2. Redis 7.2 - Used for websocket notifications and caching
  3. Penpot Backend - Clojure/JVM application
  4. Penpot Frontend - ClojureScript/React SPA
  5. Penpot Exporter - Service for generating exports
  6. Nginx - Web server and reverse proxy
  7. MailCatcher - SMTP service for development/testing

Prerequisites

System Requirements

  • Debian 12 system with sudo access
  • At least 4GB RAM (8GB+ recommended for your 64GB system)
  • 10GB+ free disk space
  • OpenJDK 17+ for running Clojure backend
  • Node.js 18+ for building frontend assets

Initial System Setup

# Update system packages
sudo apt update && sudo apt upgrade -y

# Install essential build tools and dependencies
sudo apt install -y \
    wget curl git vim unzip \
    build-essential \
    postgresql-15 postgresql-client-15 postgresql-contrib-15 \
    redis-server \
    openjdk-17-jdk \
    nodejs npm \
    nginx \
    python3 python3-pip \
    libpq-dev \
    supervisor

# Verify Java installation
java -version
javac -version

Phase 1: Database Setup

PostgreSQL Configuration

# Start and enable PostgreSQL
sudo systemctl start postgresql
sudo systemctl enable postgresql

# Create Penpot database and user
sudo -u postgres psql <<EOF
CREATE DATABASE penpot;
CREATE USER penpot WITH PASSWORD 'your_secure_password_here';
GRANT ALL PRIVILEGES ON DATABASE penpot TO penpot;
ALTER USER penpot CREATEDB;
\q
EOF

# Test the connection
psql -h localhost -U penpot -d penpot -c "SELECT version();"

Redis Configuration

# Configure Redis
sudo vim /etc/redis/redis.conf

# Key settings to verify/modify:
# bind 127.0.0.1
# port 6379
# save 900 1

# Start and enable Redis
sudo systemctl start redis-server
sudo systemctl enable redis-server

# Test Redis connection
redis-cli ping

Phase 2: Application Directory Setup

# Create application directories
sudo mkdir -p /opt/penpot/{backend,frontend,exporter,assets,logs}
sudo mkdir -p /var/log/penpot
sudo useradd -r -s /bin/false -d /opt/penpot penpot
sudo chown -R penpot:penpot /opt/penpot
sudo chown -R penpot:penpot /var/log/penpot

Phase 3: Backend Installation

Based on the Docker image analysis, the backend requires a JVM environment with the compiled Clojure application.

Download and Extract Backend

# Create backend working directory
cd /opt/penpot/backend

# Since Penpot doesn't provide pre-built JARs, we need to build from source
# First, let's clone the repository
sudo -u penpot git clone https://github.com/penpot/penpot.git /opt/penpot/source

# Install Clojure CLI tools
curl -O https://download.clojure.org/install/linux-install-1.11.1.1413.sh
chmod +x linux-install-1.11.1.1413.sh
sudo ./linux-install-1.11.1.1413.sh

# Install Leiningen for backend build
curl -o /usr/local/bin/lein https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein
# or maybe with sudo:
# sudo -s curl -o /usr/local/bin/lein https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein sudo chmod +x /usr/local/bin/lein lein version

Backend Build Process

# Navigate to backend source
cd /opt/penpot/source/backend

# Build the backend application
# This will attempt to create a standalone JAR file
sudo -u penpot lein uberjar

# If successful, copy the JAR to the backend directory
sudo -u penpot cp target/app.jar /opt/penpot/backend/

# Create backend configuration file
sudo -u penpot tee /opt/penpot/backend/config.env <<EOF
# Database Configuration
PENPOT_DATABASE_URI=postgresql://localhost/penpot
PENPOT_DATABASE_USERNAME=penpot
PENPOT_DATABASE_PASSWORD=your_secure_password_here

# Redis Configuration
PENPOT_REDIS_URI=redis://localhost/0

# Application Configuration
PENPOT_PUBLIC_URI=http://localhost:3000
PENPOT_SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_urlsafe(64))")

# Asset Storage
PENPOT_ASSETS_STORAGE_BACKEND=assets-fs
PENPOT_STORAGE_ASSETS_FS_DIRECTORY=/opt/penpot/assets

# Feature Flags
PENPOT_FLAGS=disable-email-verification enable-smtp enable-prepl-server disable-secure-session-cookies enable-registration enable-login-with-password

# SMTP Configuration (using mailcatcher for development)
PENPOT_SMTP_DEFAULT_FROM=no-reply@localhost
PENPOT_SMTP_DEFAULT_REPLY_TO=no-reply@localhost
PENPOT_SMTP_HOST=localhost
PENPOT_SMTP_PORT=1025
PENPOT_SMTP_USERNAME=
PENPOT_SMTP_PASSWORD=
PENPOT_SMTP_TLS=false
PENPOT_SMTP_SSL=false

# Telemetry
PENPOT_TELEMETRY_ENABLED=true

# Server Configuration
PENPOT_HTTP_SERVER_HOST=0.0.0.0
PENPOT_HTTP_SERVER_PORT=6060
PENPOT_HTTP_SERVER_MAX_BODY_SIZE=31457280
PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE=367001600
EOF

Phase 4: Frontend Installation

Frontend Build Process

# Navigate to frontend source
cd /opt/penpot/source/frontend

# Install Node.js dependencies
sudo -u penpot npm install

# Install shadow-cljs globally for ClojureScript compilation
sudo npm install -g shadow-cljs

# Build the frontend application
sudo -u penpot npm run build

# Copy built assets to frontend directory
sudo -u penpot cp -r resources/public/* /opt/penpot/frontend/
sudo -u penpot cp -r target/dist/* /opt/penpot/frontend/

Frontend Configuration

# Create frontend configuration
sudo -u penpot tee /opt/penpot/frontend/js/config.js <<EOF
window.penpotConfig = {
  apiBaseURL: "http://localhost:6060",
  publicURI: "http://localhost:3000",
  flags: {
    enableRegistration: true,
    enableLoginWithPassword: true,
    disableEmailVerification: true
  }
};
EOF

Phase 5: Exporter Installation

The exporter service handles PDF and image generation.

# Navigate to exporter source
cd /opt/penpot/source/exporter

# The exporter is typically a Node.js application
sudo -u penpot npm install

# Build the exporter
sudo -u penpot npm run build

# Copy to exporter directory
sudo -u penpot cp -r dist/* /opt/penpot/exporter/
sudo -u penpot cp package.json /opt/penpot/exporter/

# Install production dependencies
cd /opt/penpot/exporter
sudo -u penpot npm install --production

# Create exporter configuration
sudo -u penpot tee /opt/penpot/exporter/config.env <<EOF
PENPOT_PUBLIC_URI=http://localhost:3000
PENPOT_REDIS_URI=redis://localhost/0
PENPOT_EXPORTER_HOST=0.0.0.0
PENPOT_EXPORTER_PORT=6061
EOF

Phase 6: Web Server Configuration

Nginx Setup

# Create Nginx configuration for Penpot
sudo tee /etc/nginx/sites-available/penpot <<EOF
server {
    listen 3000;
    server_name localhost;

    # Max body size should match backend configuration
    client_max_body_size 32M;

    # Serve frontend static files
    location / {
        root /opt/penpot/frontend;
        try_files \$uri \$uri/ /index.html;
        
        # Add security headers
        add_header X-Frame-Options DENY;
        add_header X-Content-Type-Options nosniff;
        add_header X-XSS-Protection "1; mode=block";
    }

    # Proxy API requests to backend
    location /api/ {
        proxy_pass http://localhost:6060/api/;
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto \$scheme;
        
        # Timeout settings
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }

    # Websocket support for notifications
    location /ws/ {
        proxy_pass http://localhost:6060/ws/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade \$http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto \$scheme;
    }

    # Serve assets
    location /assets/ {
        alias /opt/penpot/assets/;
        expires 1y;
        add_header Cache-Control "public, immutable";
    }

    # Error and access logs
    error_log /var/log/nginx/penpot_error.log;
    access_log /var/log/nginx/penpot_access.log;
}
EOF

# Enable the site
sudo ln -s /etc/nginx/sites-available/penpot /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx

Phase 7: Service Management with Supervisor

Backend Service

sudo tee /etc/supervisor/conf.d/penpot-backend.conf <<EOF
[program:penpot-backend]
command=/usr/bin/java -jar /opt/penpot/backend/app.jar
directory=/opt/penpot/backend
user=penpot
environment=PATH="/usr/bin:/bin",$(cat /opt/penpot/backend/config.env | tr '\n' ',')
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/penpot/backend.log
stderr_logfile=/var/log/penpot/backend-error.log
EOF

Exporter Service

sudo tee /etc/supervisor/conf.d/penpot-exporter.conf <<EOF
[program:penpot-exporter]
command=/usr/bin/node index.js
directory=/opt/penpot/exporter
user=penpot
environment=PATH="/usr/bin:/bin",$(cat /opt/penpot/exporter/config.env | tr '\n' ',')
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/var/log/penpot/exporter.log
stderr_logfile=/var/log/penpot/exporter-error.log
EOF

Reload Supervisor

sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start penpot-backend
sudo supervisorctl start penpot-exporter

Phase 8: Development SMTP Setup (MailCatcher)

# Install MailCatcher for development email testing
sudo gem install mailcatcher

# Create MailCatcher service
sudo tee /etc/systemd/system/mailcatcher.service <<EOF
[Unit]
Description=MailCatcher SMTP Server
After=network.target

[Service]
Type=simple
User=penpot
ExecStart=/usr/local/bin/mailcatcher --foreground --ip 127.0.0.1 --smtp-port 1025 --http-port 1080
Restart=always

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable mailcatcher
sudo systemctl start mailcatcher

Phase 9: Database Initialization

# The backend should automatically run migrations on first startup
# Monitor the backend logs to ensure successful initialization
sudo supervisorctl tail -f penpot-backend

# If manual migration is needed:
cd /opt/penpot/backend
sudo -u penpot java -cp app.jar clojure.main -m app.migrations.sql

Phase 10: Verification and Testing

Service Status Check

# Check all services are running
sudo systemctl status postgresql redis-server nginx mailcatcher
sudo supervisorctl status

# Check port bindings
sudo netstat -tlnp | grep -E ':(3000|6060|6061|1025|1080)'

# Test database connectivity
psql -h localhost -U penpot -d penpot -c "SELECT COUNT(*) FROM pg_tables;"

# Test Redis connectivity
redis-cli ping

Application Testing

  1. Open your browser and navigate to http://localhost:3000
  2. Register a new account - emails will be captured by MailCatcher
  3. Check MailCatcher web interface at http://localhost:1080 for verification emails
  4. Test core functionality like creating projects, uploading assets, etc.

Troubleshooting

Common Issues

  1. Build Failures: The biggest challenge will likely be building the Clojure/ClojureScript applications from source. The official Docker images use pre-built artifacts that aren't publicly available.

  2. Missing Dependencies: You may need additional system libraries for the exporter service (Puppeteer/Chromium dependencies).

  3. Memory Requirements: The build process is memory-intensive. 

Alternative Approach: Extract from Docker

If building from source proves challenging, you can extract the application files from the official Docker images.

First make sure setup Docker on system:

https://docs.docker.com/engine/install/debian/

Add user to docker user group:

# Add your current user to the docker group
sudo usermod -aG docker $USER

# Apply the group changes immediately (without logout/login)
newgrp docker

 

 

# Pull official images
docker pull penpotapp/backend:latest
docker pull penpotapp/frontend:latest
docker pull penpotapp/exporter:latest

# Extract application files
docker create --name temp-backend penpotapp/backend:latest
docker cp temp-backend:/opt/app /opt/penpot/backend/
docker rm temp-backend

docker create --name temp-frontend penpotapp/frontend:latest
docker cp temp-frontend:/var/www/app /opt/penpot/frontend/
docker rm temp-frontend

docker create --name temp-exporter penpotapp/exporter:latest
docker cp temp-exporter:/opt/app /opt/penpot/exporter/
docker rm temp-exporter

Security Considerations for Production

The previous instructions were for setting this up on my Alienware m18 R2 laptop. But once I have that figured out, I am going to then try to set it up in a Debian 12 VM, such a my Xen Orchestra cloud environment and Xen VM instance running Debian 12. These are some considerations for that kind of setup:

  1. Generate secure passwords for all services
  2. Configure proper SSL/TLS certificates
  3. Set up firewall rules to restrict access
  4. Configure real SMTP service instead of MailCatcher
  5. Set up regular database backups
  6. Enable security flags like secure-session-cookies
  7. Configure proper logging and monitoring

 

 

 

 

 

Navigation