Penpot в Docker Compose: готовый файл
Если команда дизайнеров упёрлась в лимиты бесплатного Figma-плана или просто не хочет держать макеты на чужих серверах, Penpot закрывает вопрос: это полноценный open-source редактор интерфейсов с векторными инструментами, компонентами, автолейаутом и совместной работой в реальном времени — и его можно поднять на своём VPS за один запуск docker compose. Ниже — рабочий файл со всеми сервисами, объяснение переменных окружения и типичные грабли, на которые наступают при первом деплое.
Содержание
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →Что такое Penpot и когда имеет смысл его самостоятельный хостинг
Penpot — проект испанской компании Kaleidos, написан на Clojure/ClojureScript, распространяется под MPL-2.0. Умеет то же, что и Figma для базового флоу: артборды, компоненты, токены дизайна, прототипирование с переходами, экспорт в SVG/PNG/PDF, совместное редактирование через WebSocket. Из отличий — родная поддержка SVG как формата хранения фигур и отсутствие привязки к облаку конкретного вендора.
Самостоятельный хостинг имеет смысл в трёх случаях:
- команда работает с NDA-проектами или клиентскими макетами, которые нельзя держать на чужой инфраструктуре;
- нужно больше воркспейсов и участников, чем даёт бесплатный тариф облачного Penpot;
- хочется контролировать SMTP, домен, бэкапы и не зависеть от аптайма стороннего сервиса.
Минус — сопровождение теперь на вас: обновления образов, бэкапы базы, мониторинг диска под ассеты. Если команда небольшая и не хранит ничего чувствительного, облачная версия design.penpot.app может быть проще. Но для агентства или продуктовой команды, которая уже держит инфраструктуру на своих серверах, self-hosted вариант обычно окупается быстро.
Требования к серверу и подготовка
Penpot — это пять контейнеров: frontend (nginx-обёртка над статикой), backend (Clojure-приложение, основная логика), exporter (headless Chromium для экспорта в PDF/PNG), PostgreSQL и Redis. Из них exporter — самый прожорливый по памяти именно из-за встроенного браузера, закладывайте под него отдельный запас, а не рассчитывайте, что общих 512 МБ на всё хватит.
Ориентировочные требования, которые стоит проверить под свою нагрузку:
| Сценарий | vCPU | RAM | Диск |
|---|---|---|---|
| Соло-дизайнер / тест | 2 | 4 ГБ | 20 ГБ SSD |
| Команда до 10 человек | 2-4 | 8 ГБ | 40-60 ГБ SSD |
| Студия, активные экспорты, много ассетов | 4+ | 16 ГБ | от 100 ГБ SSD |
Это отправная точка, а не измеренный бенчмарк — реальное потребление зависит от размера файлов и количества одновременных сессий редактирования. На старте лучше взять план с запасом по RAM и следить за docker stats первую неделю.
Понадобится Docker и Docker Compose plugin. Если сервер только что развёрнут, порядок настройки продакшен-окружения с Docker Compose подробно разобран в статье про подготовку сервера под Docker Compose — там же про firewall и своп-файл, которые пригодятся и здесь.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверГотовый docker-compose.yml
Структура повторяет официальный compose-файл проекта Penpot, адаптированный под типовой VPS с одним доменом:
services:
penpot-frontend:
image: penpotapp/frontend:latest
restart: always
ports:
- "9001:8080"
volumes:
- penpot_assets:/opt/data/assets
depends_on:
- penpot-backend
- penpot-exporter
env_file:
- .env
penpot-backend:
image: penpotapp/backend:latest
restart: always
volumes:
- penpot_assets:/opt/data/assets
depends_on:
- penpot-postgres
- penpot-redis
env_file:
- .env
penpot-exporter:
image: penpotapp/exporter:latest
restart: always
environment:
- PENPOT_PUBLIC_URI=http://penpot-frontend:8080
- PENPOT_REDIS_URI=redis://penpot-redis/0
penpot-postgres:
image: postgres:15
restart: always
stop_signal: SIGINT
volumes:
- penpot_postgres_v15:/var/lib/postgresql/data
environment:
- POSTGRES_INITDB_ARGS=--data-checksums
- POSTGRES_DB=penpot
- POSTGRES_USER=penpot
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
penpot-redis:
image: redis:7
restart: always
volumes:
penpot_postgres_v15:
penpot_assets:
Порт 9001 наружу — временно, для первой проверки через curl или SSH-туннель. В проде фронтенд закрывается за reverse-proxy, об этом ниже. Тег latest удобен для старта, но на боевом сервере лучше зафиксировать конкретную версию (например, penpotapp/backend:2.5.0) — актуальный список тегов смотрите на Docker Hub на момент установки, у проекта активный релизный цикл и версии за август 2026 могут отличаться от тех, что были на момент написания этой статьи.
Переменные окружения: файл .env
Все настройки Penpot передаются через переменные с префиксом PENPOT_. Создайте .env рядом с docker-compose.yml:
# Публичный адрес, под которым будет доступен Penpot
PENPOT_PUBLIC_URI=https://design.example.com
# Секретный ключ для подписи сессий — сгенерировать один раз и не менять
PENPOT_SECRET_KEY=замените-на-вывод-openssl-rand--hex-32
# Подключение к базе
PENPOT_DATABASE_URI=postgresql://penpot-postgres/penpot
PENPOT_DATABASE_USERNAME=penpot
PENPOT_DATABASE_PASSWORD=сложный-пароль-для-postgres
POSTGRES_PASSWORD=сложный-пароль-для-postgres
# Redis для пуб-саба между backend и exporter
PENPOT_REDIS_URI=redis://penpot-redis/0
# Хранилище ассетов на диске контейнера (том penpot_assets)
PENPOT_ASSETS_STORAGE_BACKEND=assets-fs
PENPOT_STORAGE_ASSETS_FS_DIRECTORY=/opt/data/assets
# Флаги функциональности через пробел
PENPOT_FLAGS=enable-registration enable-login-with-password disable-onboarding-questions enable-smtp
# Телеметрия — по умолчанию можно отключить
PENPOT_TELEMETRY_ENABLED=false
# SMTP для писем с приглашениями и подтверждением почты
PENPOT_SMTP_DEFAULT_REPLY_TO=noreply@example.com
PENPOT_SMTP_DEFAULT_FROM=noreply@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
PENPOT_SECRET_KEY сгенерируйте один раз командой:
openssl rand -hex 32
и больше не меняйте — иначе слетят активные сессии всех пользователей. Пароли для Postgres задавайте отдельно от секретного ключа, не переиспользуйте один пароль в разных сервисах.
Если SMTP не настроен, оставьте флаг enable-registration без enable-smtp — тогда работает регистрация без подтверждения почты, но и без писем-приглашений (придётся выдавать доступ вручную через админку или отключать регистрацию совсем и заводить пользователей самостоятельно).
Запуск и создание первого аккаунта
Поднимаем стек:
docker compose up -d
docker compose logs -f penpot-backend
Backend при первом старте сам накатывает миграции на пустую базу — это может занять минуту-две, дождитесь строки о готовности слушать порт. После этого:
curl -I http://localhost:9001
должен вернуть 200 OK. Дальше откройте http://ip-сервера:9001 в браузере (или сразу настроенный домен, если уже дошли до этого шага) и зарегистрируйте первый аккаунт — он не становится админом автоматически, Penpot не разделяет роль «суперпользователь» на уровне UI, управление правами происходит на уровне команд (teams) внутри приложения.
Если публичная регистрация не нужна вообще (типичный случай для закрытой команды), уберите enable-registration из PENPOT_FLAGS и добавьте disable-registration — новых пользователей заводите через приглашение из уже существующей команды по email (тогда SMTP обязателен) либо создавайте их вручную через backend-консоль.
Домен, HTTPS и reverse-proxy
Открывать Penpot напрямую по порту 9001 без TLS не стоит — это рабочий инструмент с логинами и паролями участников команды. Проще всего поставить перед стеком Traefik или nginx с автоматическим Let's Encrypt.
Если уже используете Traefik для других контейнеров на сервере, добавьте лейблы прямо в сервис penpot-frontend и уберите проброс порта 9001:8080 наружу — трафик пойдёт через сеть Traefik. Как именно поднять Traefik перед Docker-контейнерами и подключить его к общей сети, подробно описано в статье про Traefik как reverse-proxy для Docker.
Пример лейблов для penpot-frontend:
labels:
- "traefik.enable=true"
- "traefik.http.routers.penpot.rule=Host(`design.example.com`)"
- "traefik.http.routers.penpot.entrypoints=websecure"
- "traefik.http.routers.penpot.tls.certresolver=letsencrypt"
- "traefik.http.services.penpot.loadbalancer.server.port=8080"
Важный нюанс с WebSocket: совместное редактирование в реальном времени идёт через WS-соединение к backend, и если проксируете через голый nginx без апгрейда заголовков Connection/Upgrade, курсоры коллег просто не будут обновляться живьём, хотя сам редактор откроется. Traefik это обрабатывает из коробки, а для nginx нужно явно прописывать proxy_set_header Upgrade $http_upgrade; в блоке location.
Резервное копирование, обновления и типичные проблемы
Данные, которые нужно бэкапить, — это два тома: penpot_postgres_v15 (сама база — команды, файлы, история) и penpot_assets (загруженные изображения, шрифты, экспорты). Простой дамп базы:
docker compose exec penpot-postgres pg_dump -U penpot penpot > penpot_$(date +%F).sql
Ассеты можно бэкапить обычным tar по тому, либо сразу вынести их в S3-совместимое хранилище через PENPOT_ASSETS_STORAGE_BACKEND=assets-s3, что снимает вопрос бэкапа файлов вообще — тогда достаточно бэкапить только базу. Поднять для этого собственный S3-совместимый бакет на том же сервере можно через MinIO, готовый compose-файл под него разобран в статье MinIO в Docker Compose.
Частые проблемы при первом деплое:
- Backend не стартует, в логах ошибки миграции — обычно означает, что Postgres ещё не готов принимать соединения к моменту старта backend.
depends_onв Compose не ждёт готовности сервиса, а только запуска контейнера — если проблема повторяется, добавьтеrestart: on-failureна backend или healthcheck на Postgres. - Экспорт в PDF зависает или контейнер exporter перезапускается по OOM — headless-браузеру внутри тесно в лимите памяти, поднимите
mem_limitдля сервиса или общий RAM хоста. - Письма-приглашения не приходят — самая частая причина не в Penpot, а в блокировке исходящего SMTP-трафика хостером или отсутствии обратной DNS-записи (PTR) у IP сервера; почтовые провайдеры молча дропают такие письма.
- После смены
PENPOT_PUBLIC_URIссылки в письмах и экспортах остаются старыми — переменную нужно менять и в backend, и в exporter (в примере выше exporter получает её отдельной строкой), а не только в общем.env.
Обновление — стандартный docker-цикл:
docker compose pull
docker compose up -d
Перед обновлением на мажорную версию стоит сделать дамп базы отдельно и свериться с changelog проекта — миграции схемы в Penpot иногда необратимы без отката из бэкапа.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.
Арендовать серверНужны сами нейросети для контента?
Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.
Частые вопросы
Penpot можно использовать без интернета, полностью локально?
Да, весь стек работает автономно на своём сервере или даже в локальной сети без выхода наружу — понадобится только внутренний DNS или файл hosts для доступа по домену, и самоподписанный сертификат вместо Let's Encrypt.
Нужен ли отдельный сервер под каждый компонент?
Нет, все пять сервисов спокойно живут на одном VPS с ресурсами из таблицы выше — разносить их по разным машинам имеет смысл только при заметной нагрузке или требованиях по изоляции.
Чем self-hosted Penpot принципиально отличается от облачного design.penpot.app?
Кодовая база и функциональность те же — отличие в том, что данные, домен, SMTP, лимиты команд и участников полностью под вашим контролем, а поддержку и обновления берёте на себя вы.
Можно ли мигрировать проекты из Figma?
Penpot поддерживает импорт файлов Figma через официальный плагин экспорта в промежуточный формат, но сложные конструкции (некоторые плагины, продвинутые auto-layout сценарии) переносятся не идеально — перед массовой миграцией стоит проверить критичные файлы вручную.
Что будет с данными, если контейнеры удалить командой docker compose down?
Сами контейнеры пересоздаются без потерь, пока целы volumes penpot_postgres_v15 и penpot_assets — удаляются они только явной командой docker compose down -v, поэтому эту команду с флагом -v на проде лучше вообще не использовать по привычке.
Обсудить статью, задать вопрос или начать новую тему
Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.
Перейти в сообщество →