Как установить и настроить Penpot на VPS
Figma закрыла доступ или подняла цены за место в команде — а макеты и дизайн-система остаются такими же критичными, как раньше. Penpot — открытый редактор интерфейсов и прототипов, который работает в браузере почти так же, как Figma, но крутится на вашем сервере: файлы никуда не улетают, лимит мест в команде задаёте сами, а оплата зависит только от аренды VPS. Ниже — рабочий способ поднять Penpot через Docker Compose, с HTTPS через nginx и понятной схемой бэкапов.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что реально нужно от сервера
Penpot — это не один контейнер, а связка из пяти сервисов: фронтенд (SPA плюс встроенный nginx, проксирующий запросы к остальным частям), backend на Clojure/JVM, exporter для рендера PDF/PNG/SVG через headless-браузер, PostgreSQL для данных и Redis для очередей и realtime-уведомлений между открытыми вкладками редактора.
Ориентир по ресурсам для команды в несколько человек:
| Параметр | Минимум | Комфортно |
|---|---|---|
| CPU | 2 vCPU | 4 vCPU |
| RAM | 4 ГБ | 8 ГБ |
| Диск | 20 ГБ SSD | 40+ ГБ SSD/NVMe |
| ОС | Ubuntu 24.04 / Debian 12 | Ubuntu 24.04 |
Backend на JVM держит заметный базовый расход памяти даже без нагрузки, а exporter при пакетном экспорте больших досок в PDF временно добавляет CPU и RAM на рендер headless Chrome. На 2 ГБ RAM стек формально стартует, но backend и exporter начинают конкурировать за память при первой же работе с крупным файлом — берите от 4 ГБ как стартовую точку. Точных цифр по отклику редактора намеренно не привожу — они сильно зависят от размера файлов и числа пользователей, замеряйте на своей нагрузке.
Понадобится домен: Penpot жёстко завязан на публичный URL (PENPOT_PUBLIC_URI) — без него realtime-совместная работа и генерация ссылок на файлы работают некорректно. Заранее направьте A-запись домена на IP сервера.
Подготовка сервера и Docker
Работаем от пользователя с правами sudo, не от root.
apt update && apt upgrade -y
apt install -y curl git ca-certificates gnupg openssl
# Docker Engine + Compose plugin
curl -fsSL https://get.docker.com | sh
usermod -aG docker $USER
newgrp docker
docker --version
docker compose version
Важно, чтобы это был именно Compose-плагин (команда docker compose, без дефиса) — файл ниже рассчитан на синтаксис Compose v2. Если сервер уже держит несколько контейнерных стеков и вы ещё не настраивали общие лимиты ресурсов и restart-политики, есть смысл сначала пройтись по статье Docker Compose для продакшена — там разобраны практики, которые пригодятся и здесь.
Создайте рабочую директорию и сгенерируйте секретный ключ, которым backend подписывает сессии и токены:
mkdir -p /opt/penpot && cd /opt/penpot
openssl rand -hex 64
Полученную строку сохраните — она пойдёт в переменную PENPOT_SECRET_KEY ниже. Потерять её после запуска — значит разлогинить всех пользователей и инвалидировать выданные токены доступа.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверdocker-compose.yml: собираем стек Penpot
Файл ниже — рабочий минимум под один домен, с локальным файловым хранилищем ассетов (без внешнего S3/MinIO, для старта этого достаточно):
services:
penpot-frontend:
image: penpotapp/frontend:latest
restart: unless-stopped
ports:
- "8080:8080"
volumes:
- penpot_assets:/opt/data/assets
depends_on:
- penpot-backend
- penpot-exporter
penpot-backend:
image: penpotapp/backend:latest
restart: unless-stopped
volumes:
- penpot_assets:/opt/data/assets
environment:
- PENPOT_FLAGS=enable-registration disable-email-verification enable-smtp
- PENPOT_SECRET_KEY=вставьте_сгенерированный_ключ
- PENPOT_PUBLIC_URI=https://penpot.example.com
- PENPOT_DATABASE_URI=postgresql://penpot-postgres/penpot
- PENPOT_DATABASE_USERNAME=penpot
- PENPOT_DATABASE_PASSWORD=надёжный_пароль_бд
- PENPOT_REDIS_URI=redis://penpot-redis/0
- PENPOT_ASSETS_STORAGE_BACKEND=assets-fs
- PENPOT_STORAGE_ASSETS_FS_DIRECTORY=/opt/data/assets
- PENPOT_TELEMETRY_ENABLED=false
depends_on:
- penpot-postgres
- penpot-redis
penpot-exporter:
image: penpotapp/exporter:latest
restart: unless-stopped
environment:
- PENPOT_PUBLIC_URI=https://penpot.example.com
penpot-postgres:
image: postgres:15
restart: unless-stopped
environment:
- POSTGRES_DB=penpot
- POSTGRES_USER=penpot
- POSTGRES_PASSWORD=надёжный_пароль_бд
- POSTGRES_INITDB_ARGS=--data-checksums
volumes:
- penpot_postgres:/var/lib/postgresql/data
penpot-redis:
image: redis:7
restart: unless-stopped
volumes:
penpot_assets:
penpot_postgres:
Пароль в PENPOT_DATABASE_PASSWORD и POSTGRES_PASSWORD должен совпадать буквально — это одна учётка, backend подключается к Postgres по имени сервиса в Docker-сети. Тег latest на старте удобен, но для продакшена лучше сразу зафиксировать версию образов на Docker Hub (penpotapp/frontend, penpotapp/backend, penpotapp/exporter), чтобы апдейт не прилетал незаметно вместе с docker compose pull.
Поднимаем стек:
docker compose up -d
docker compose ps
docker compose logs -f penpot-backend
Backend на первом старте сам накатывает миграции базы — это видно в логах по строкам о применении migrations. Дождитесь, пока контейнер перейдёт в стабильный running без циклов рестарта, и только потом идите настраивать nginx.
Переменные окружения: домен, почта и регистрация
Флаги в PENPOT_FLAGS — это строка через пробел, включающая или выключающая функциональность backend. В примере выше:
enable-registration— разрешает публичную самостоятельную регистрацию по ссылке. Для закрытой команды его стоит убрать и приглашать участников только через админ-панель или инвайты по почте.disable-email-verification— убирает обязательное подтверждение email при регистрации. Удобно, пока SMTP не настроен, но для продакшена лучше включить проверку и настроить почту.enable-smtp— включает отправку писем (подтверждение регистрации, приглашения, сброс пароля).
Список флагов между версиями Penpot менялся — перед установкой сверьтесь с актуальным .env.example в репозитории проекта на GitHub.
Для отправки писем добавьте в penpot-backend блок SMTP:
- PENPOT_SMTP_DEFAULT_FROM=no-reply@example.com
- PENPOT_SMTP_DEFAULT_REPLY_TO=no-reply@example.com
- PENPOT_SMTP_HOST=smtp.example.com
- PENPOT_SMTP_PORT=587
- PENPOT_SMTP_USERNAME=smtp-user
- PENPOT_SMTP_PASSWORD=smtp-password
- PENPOT_SMTP_TLS=true
Без рабочего SMTP приглашения в закрытую команду и восстановление пароля не дойдут до получателя — письмо уйдёт в логи backend вместо почтового ящика. Если своего SMTP-сервера нет, проще и надёжнее использовать транзакционный сервис стороннего провайдера, чем поднимать почтовый сервер ради нескольких писем в день.
После правки .env-переменных или самого compose-файла применяйте их так:
docker compose up -d --force-recreate penpot-backend penpot-frontend penpot-exporter
Nginx как reverse proxy и HTTPS
Фронтенд-контейнер слушает 8080 на localhost — наружу пускаем его через nginx с сертификатом Let's Encrypt, ровно как для любого другого self-hosted сервиса за прокси:
server {
listen 80;
server_name penpot.example.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name penpot.example.com;
ssl_certificate /etc/letsencrypt/live/penpot.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/penpot.example.com/privkey.pem;
client_max_body_size 50m;
location / {
proxy_pass http://127.0.0.1:8080;
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;
}
}
Заголовки Upgrade/Connection "upgrade" здесь не для галочки: realtime-совместная работа над доской идёт через WebSocket, и без корректного апгрейда соединения коллеги не увидят ваши правки в реальном времени, хотя страница загрузится без ошибок. client_max_body_size увеличен, потому что импорт файлов из Figma и загрузка изображений в библиотеку легко превышают дефолтный 1 МБ nginx.
Если nginx как reverse proxy для вас в новинку — базовые конфиги и логика разобраны в статье Nginx как reverse proxy, а получение сертификата через certbot — в статье Let's Encrypt SSL на VPS:
apt install -y certbot python3-certbot-nginx
certbot --nginx -d penpot.example.com
После выпуска сертификата убедитесь, что PENPOT_PUBLIC_URI в compose-файле указывает на https://, а не на http:// — иначе ссылки на файлы и приглашения будут генерироваться с неправильной схемой.
Бэкапы: база данных и файлы дизайна
В self-hosted Penpot за сохранность макетов отвечаете вы — данные лежат в двух местах, и важны оба: PostgreSQL хранит структуру файлов, страницы, компоненты и пользователей, а volume penpot_assets — бинарники: изображения, шрифты, превью.
mkdir -p /opt/penpot-backups
# дамп базы
docker compose exec -T penpot-postgres pg_dump -U penpot penpot \
| gzip > /opt/penpot-backups/penpot-db-$(date +%F).sql.gz
# файлы ассетов (том Docker, не директория на хосте)
docker run --rm \
-v penpot_penpot_assets:/data \
-v /opt/penpot-backups:/backup \
alpine tar czf /backup/penpot-assets-$(date +%F).tar.gz -C /data .
Имя тома в docker run зависит от имени проекта Compose (обычно <имя_директории>_penpot_assets — проверьте через docker volume ls). Бэкап без одной из двух частей бесполезен: только база — картинки и шрифты станут битыми ссылками; только assets — останется архив бинарников без единой записи о том, какому файлу они принадлежат.
Автоматизацию через cron и логику снапшотов Docker-томов подробно разбирает статья бэкап Docker volume на VPS — переносится на Penpot один в один.
Обновление и обслуживание
Penpot развивается активно, и в новых релизах чинят как баги редактора, так и уязвимости backend. Обновление стандартное для Docker-стека:
cd /opt/penpot
docker compose pull
docker compose up -d
docker compose logs -f penpot-backend
Перед обновлением снимите свежий дамп базы — миграции применяются автоматически при старте backend и в большинстве случаев необратимы без отката из бэкапа. Если фиксируете версии тегами, проверяйте changelog проекта на GitHub на breaking changes перед апдейтом на мажорную версию.
Раз в несколько недель проверяйте место на диске — PostgreSQL и volume ассетов растут вместе с числом файлов:
docker system df
docker exec penpot-postgres psql -U penpot -d penpot -c "SELECT pg_size_pretty(pg_database_size('penpot'));"
Если база разрослась заметно, а команда давно не чистила старые версии файлов и превью — загляните в статью про тюнинг PostgreSQL: дефолтные настройки Postgres на небольшом VPS не рассчитаны на активную запись многих версий документов одновременно.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Чем self-hosted Penpot принципиально отличается от Figma для команды?
Функционально редактор близок к Figma — векторные слои, компоненты, авто-лейауты, прототипирование, экспорт в SVG/PNG/PDF. Главные отличия: файлы и история версий хранятся на вашем сервере, лимит мест не завязан на тариф вендора, а работа с внешними подрядчиками требует либо открытой регистрации, либо приглашений через SMTP.
Нужен ли отдельный S3/MinIO для хранения файлов или хватит локального диска?
Для одного сервера и одной команды локального volume (assets-fs) достаточно — так настроен пример выше. S3-хранилище (PENPOT_ASSETS_STORAGE_BACKEND=s3) имеет смысл, если масштабируете backend на несколько инстансов.
Почему совместное редактирование доски работает с задержкой или не работает вообще?
Чаще всего причина — неправильный WebSocket-апгрейд в nginx (нет заголовков Upgrade/Connection) или несовпадение PENPOT_PUBLIC_URI со схемой, по которой реально открыт сайт (http вместо https).
Можно ли перенести существующие макеты из Figma в свой Penpot?
Прямого импорта аккаунта нет, но Penpot поддерживает импорт файлов .fig, экспортированных из Figma — простые макеты переносятся хорошо, сложные плагины и специфичные эффекты могут потребовать ручной доработки после импорта.
Сколько человек выдержит минимальная конфигурация в 4 ГБ RAM?
Для небольшой команды (3-7 человек без постоянной параллельной работы над одним огромным файлом) обычно достаточно. При регулярных пакетных экспортах через exporter закладывайте запас RAM отдельно под этот сервис.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →