Удельта

Пошаговая инструкция · Самостоятельная установка

Как установить Plane на Ubuntu и запустить работу команды

Пошаговая инструкция для самостоятельной установки. Развернём Plane Community Edition на отдельном сервере, настроим HTTPS и безопасные параметры, создадим первый проект и подготовим копии задач с вложениями.

Первый проект: задачи распределены по этапам, а проектный процесс соединён с собственным хранилищем

Что подготовить и как читать инструкцию #

Устанавливаем открытый Plane Community Edition, а не Commercial Edition на бесплатном плане. У этих редакций разные установщики и команды обслуживания. Для Community используем setup.sh и файлы одного стабильного релиза 1.4.2. Общее устройство работы с задачами и проектами описано на странице Plane.

Начальная оценка для небольшой команды — 4 vCPU, 8 ГБ RAM и SSD от 80 ГБ. Plane состоит из нескольких сервисов: web, API, обработчиков, PostgreSQL, кеша, очереди и хранилища вложений. Поэтому свободной памяти должно хватать всей установке, а не только открытому сайту. Подберите ресурсы по руководству выбора сервера Plane и предусмотрите место для роста базы, файлов и временной копии.

Команды выполняйте в терминале сервера после подключения по SSH, если шаг не указывает другое место. sudo даёт команде права администратора. Действия в браузере выполняйте на своём компьютере. Домены, IP-адреса и значения ВАШ_ПАРОЛЬ из примеров заменяйте своими до выполнения команды.

Двигайтесь по одному шагу и проверяйте результат перед следующим. Блок с <<EOF или другим именем завершения копируйте целиком: он записывает содержимое одного файла. При работе в nano сохраняйте файл клавишами Ctrl+O, Enter и выходите Ctrl+X. Если прервали SSH-сеанс, снова перейдите в указанный каталог; переменные предыдущего сеанса потребуется задать заново.

Подготовьте Nginx и Certbot на хосте по общей инструкции. Внешний Nginx обслуживает HTTPS, внутренний контейнер Nginx распределяет запросы по сервисам Plane. Это замена штатного proxy в официальном Compose; остальные сервисы и тома сохраняются.

Дополнительные официальные источники: Поддержка Ubuntu в Docker, Теги официального образа Nginx.

Шаг 1. Создайте каталоги Plane #

Комплект установки будет в /opt/plane-selfhost, файлы приложения — в plane-app. Не путайте каталог установки с файлами самого проекта.

Создайте каталоги Plane
sudo install -d -o "$USER" -g "$(id -gn)" -m 750 /opt/plane-selfhost
cd /opt/plane-selfhost
mkdir -p plane-app

Вы находитесь в /opt/plane-selfhost.

Шаг 2. Скачайте управляющие скрипты #

setup.sh управляет этой установкой, restore.sh используется для восстановления. Берём оба из релиза v1.4.2. Они пока только скачиваются.

Скачайте управляющие скрипты
curl -fL -o setup.sh https://github.com/makeplane/plane/releases/download/v1.4.2/setup.sh
curl -fL -o restore.sh https://github.com/makeplane/plane/releases/download/v1.4.2/restore.sh

Оба скрипта скачаны без ошибки HTTP.

Шаг 3. Скачайте Compose и переменные #

Сохраним описание сервисов и образец plane.env того же релиза. Не смешивайте файлы разных выпусков.

Скачайте Compose и переменные
curl -fL -o plane-app/docker-compose.yaml https://github.com/makeplane/plane/releases/download/v1.4.2/docker-compose.yml
curl -fL -o plane-app/plane.env https://github.com/makeplane/plane/releases/download/v1.4.2/variables.env

В plane-app находятся docker-compose.yaml и plane.env.

Шаг 4. Настройте доступ к файлам #

Скриптам нужно право запуска, файлу с секретами — ограниченное чтение. Перед запуском можно посмотреть скрипт через less setup.sh; выход — q.

Настройте доступ к файлам
chmod 750 setup.sh restore.sh
chmod 600 plane-app/plane.env

Скрипты доступны владельцу, plane.env защищён правами 600.

Шаг 5. Подготовьте шесть случайных значений #

Повторите openssl rand -hex 32 шесть раз: значения нужны ключам приложения, совместного редактора, базы, очереди и двум реквизитам локального S3-совместимого хранилища. Это разные секреты, не один общий пароль.

Подготовьте шесть случайных значений
openssl rand -hex 32

Шесть значений сохранены в менеджере паролей.

Шаг 6. Заполните plane.env #

Откройте nano plane-app/plane.env и измените строки по образцу. Каждый REPLACE замените своим отдельным секретом, домен — своим адресом. Публичные URL используют HTTPS внешнего Nginx. CERT_EMAIL и параметры встроенного сертификата здесь не используются: его выпускает Certbot на хосте.

Заполните plane.env
APP_DOMAIN=plane.example.com
APP_RELEASE=v1.4.2
WEB_URL=https://plane.example.com
CORS_ALLOWED_ORIGINS=https://plane.example.com
SITE_ADDRESS=:80
CERT_EMAIL=
LISTEN_HTTP_PORT=8180
SECRET_KEY=REPLACE_WITH_RANDOM_APP_SECRET
LIVE_SERVER_SECRET_KEY=REPLACE_WITH_RANDOM_LIVE_SECRET
POSTGRES_PASSWORD=REPLACE_WITH_RANDOM_DB_PASSWORD
RABBITMQ_PASSWORD=REPLACE_WITH_RANDOM_QUEUE_PASSWORD
AWS_ACCESS_KEY_ID=REPLACE_WITH_RANDOM_STORAGE_ID
AWS_SECRET_ACCESS_KEY=REPLACE_WITH_RANDOM_STORAGE_SECRET
DEBUG=0

Все REPLACE заменены; PUBLIC URL и APP_DOMAIN относятся к одному домену.

Шаг 7. Замените контейнер proxy на Nginx #

Откройте nano plane-app/docker-compose.yaml. Внутри services замените только целиком блок proxy приведённым ниже: от его строки proxy до следующего раздела верхнего уровня volumes. Прежние блоки web, api, базы и остальных сервисов сохраняйте. proxy слушает локальный 8180; 80/443 остаются Nginx на хосте.

Новый services.proxy в plane-app/docker-compose.yaml
  proxy:
    image: nginx:1.30.5-alpine
    restart: unless-stopped
    ports:
      - "127.0.0.1:8180:80"
    volumes:
      - ./nginx-router.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      - web
      - api
      - space
      - admin
      - live
      - plane-minio

Сохраните файл. Имена сервисов не меняются; прежние proxy_config/proxy_data можно оставить в списке volumes — они не подключены к новому Nginx.

Шаг 8. Сохраните маршруты сервисов Plane #

Откройте nano plane-app/nginx-router.conf и сохраните файл ниже. Он повторяет маршруты релиза: /spaces — публичные проекты, /god-mode — настройки установки, /live — совместная работа, /api и /auth — сервер, /uploads — вложения. Не удаляйте префиксы путей. В нашем plane.env имя бакета AWS_S3_BUCKET_NAME=uploads; если поменяли его, поменяйте и маршрут. Внутренний Nginx получает заголовки только от внешнего Nginx, который их перезаписывает.

Файл plane-app/nginx-router.conf
map $http_upgrade $plane_internal_upgrade {
    default upgrade;
    "" close;
}
server {
    listen 80;
    client_max_body_size 5m;
    location = /spaces { return 301 /spaces/; }
    location = /god-mode { return 301 /god-mode/; }
    location /spaces/ {
        proxy_pass http://space:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $http_x_real_ip;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-For $http_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $plane_internal_upgrade;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
        proxy_buffering off;
    }
    location /god-mode/ {
        proxy_pass http://admin:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $http_x_real_ip;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-For $http_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $plane_internal_upgrade;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
        proxy_buffering off;
    }
    location /live/ {
        proxy_pass http://live:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $http_x_real_ip;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-For $http_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $plane_internal_upgrade;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
        proxy_buffering off;
    }
    location /api/ {
        proxy_pass http://api:8000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $http_x_real_ip;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-For $http_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $plane_internal_upgrade;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
        proxy_buffering off;
    }
    location /auth/ {
        proxy_pass http://api:8000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $http_x_real_ip;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-For $http_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $plane_internal_upgrade;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
        proxy_buffering off;
    }
    location /static/ {
        proxy_pass http://api:8000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $http_x_real_ip;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-For $http_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $plane_internal_upgrade;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
        proxy_buffering off;
    }
    location /uploads {
        proxy_pass http://plane-minio:9000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $http_x_real_ip;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-For $http_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $plane_internal_upgrade;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
        proxy_buffering off;
        proxy_request_buffering off;
    }
    location / {
        proxy_pass http://web:3000;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $http_x_real_ip;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-For $http_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $http_x_forwarded_proto;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $plane_internal_upgrade;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
        proxy_buffering off;
    }
}

Файл находится рядом с docker-compose.yaml. Лимит 5 МБ соответствует FILE_SIZE_LIMIT=5242880; если увеличиваете вложения, согласуйте оба значения.

Шаг 9. Направьте домен на сервер #

Создайте A-запись plane.example.com с публичным IP сервера. Порты 80/443 обслуживает Nginx на хосте, внутренний router доступен только на 127.0.0.1:8180.

Направьте домен на сервер
getent ahostsv4 plane.example.com
sudo ss -lntp

DNS возвращает ваш IP; у провайдера разрешены 80/443. Внешний доступ к 8180 не открывайте.

Шаг 10. Проверьте конфигурацию #

Проверим именно файлы из plane-app с указанным env-file. Без этого Compose может прочитать другой набор переменных.

Проверьте конфигурацию
cd /opt/plane-selfhost
sudo docker compose -f plane-app/docker-compose.yaml --env-file plane-app/plane.env config --quiet

Команда завершилась без ошибки и отсутствующих переменных.

Шаг 11. Запустите официальный комплект #

setup.sh start запускает согласованные компоненты и подготовку базы. Первый запуск может занять несколько минут.

Запустите официальный комплект
sudo ./setup.sh start

Скрипт завершился успешно; не запускайте повторную установку во время работы.

Шаг 12. Дождитесь готовности сервиса #

Посмотрим контейнеры и миграцию базы. Migrator может завершиться после успешной миграции; API и новый proxy должны работать постоянно. Пока проверяем локальный HTTP, HTTPS будет следующим шагом.

Дождитесь готовности сервиса
sudo docker compose -f plane-app/docker-compose.yaml --env-file plane-app/plane.env ps
sudo docker compose -f plane-app/docker-compose.yaml --env-file plane-app/plane.env logs --tail=80 migrator api proxy
curl -I http://127.0.0.1:8180/

Локальный адрес возвращает HTTP 200 или перенаправление; API и proxy не перезапускаются. Ошибка Nginx host not found требует проверить имя сервиса в Compose.

Шаг 13. Сохраните сайт Nginx #

Установите Nginx и Certbot по подготовке Ubuntu, если ещё не установили. Откройте sudo nano /etc/nginx/sites-available/plane и вставьте файл ниже. Замените все домены своими. Nginx передаёт приложению домен, адрес клиента и протокол; Upgrade и Connection сохраняют WebSocket. До получения сертификата условие $scheme блокирует обычные HTTP-запросы кодом 403, чтобы вход не происходил без шифрования. WebSocket проходит через оба Nginx без удаления /live/. Внутренние маршруты уже настроены в nginx-router.conf; внешний прокси сохраняет исходный URI.

Файл /etc/nginx/sites-available/plane
map $http_upgrade $plane_connection_upgrade {
    default upgrade;
    "" close;
}

server {
    listen 80;
    server_name plane.example.com;
    client_max_body_size 5m;
    location / {
        if ($scheme = http) { return 403; }
        proxy_pass http://127.0.0.1:8180;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $plane_connection_upgrade;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
        proxy_buffering off;
    }
}

Сохраните Ctrl+O, Enter, Ctrl+X. В файле пока нет путей к сертификатам: Nginx сможет проверить его до выпуска HTTPS.

Шаг 14. Подключите сайт и проверьте HTTP #

Ссылка подключает сохранённый сайт. nginx -t проверяет все настройки сервера; && разрешает перечитать их только при успешной проверке. curl проверяет ответ для вашего домена. При ошибке остановитесь, исправьте указанную строку и повторите проверку.

Подключите сайт и проверьте HTTP
sudo ln -sfn /etc/nginx/sites-available/plane /etc/nginx/sites-enabled/plane
sudo nginx -t && sudo systemctl reload nginx
curl -I http://plane.example.com/

Ожидается successful от nginx -t и HTTP 403 для обычного запроса: до HTTPS это намеренная блокировка. DNS должен указывать на сервер, TCP 80/443 — быть доступен у провайдера. Ничего не вводите в форму входа по HTTP.

Шаг 15. Получите сертификат и включите HTTPS #

Каждое имя из команды должно указывать на этот сервер. Certbot проверит домены, сам добавит сертификат в сайт Nginx и включит перенаправление HTTP → HTTPS. Укажите свою почту при запросе и подтвердите условия Let’s Encrypt. Это отдельное действие после успешной проверки HTTP.

Получите сертификат и включите HTTPS
sudo /snap/bin/certbot --nginx -d plane.example.com --redirect

Certbot сообщил об успешной установке. Откройте HTTPS-адрес со своего компьютера: браузер не должен предупреждать о сертификате. Для закрытой панели используйте разрешённый IP. При ошибке исправьте DNS, в том числе AAAA, либо доступ к TCP 80; не повторяйте запросы выпуска подряд.

Шаг 16. Проверьте продление сертификата #

Certbot из snap создаёт службу автоматического продления. Dry-run выполняет пробное продление: настоящие сертификаты не заменяются. Затем проверьте, что таймер планирует следующий запуск. Сохраняйте доступ к TCP 80 для следующих проверок домена.

Проверьте продление сертификата
sudo /snap/bin/certbot renew --dry-run
systemctl list-timers --all 'snap.certbot.renew*'

Пробное продление успешно, у таймера есть следующий запуск. Если тест не прошёл, проверьте /var/log/letsencrypt/letsencrypt.log; сначала исправьте причину. После этого можно создавать учётную запись и приглашать пользователей.

Шаг 17. Создайте владельца #

Откройте свой HTTPS-домен и завершите первоначальную настройку владельца. Перед публичным доступом выберите правила регистрации и приглашений организации.

Вы входите в административную часть своей установки.

Шаг 18. Создайте рабочее пространство #

Создайте workspace для тестовой команды, затем проект внутри него. Workspace объединяет участников, проект — задачи конкретной работы.

Проект появляется в нужном workspace.

Шаг 19. Добавьте первую задачу #

В проекте создайте задачу с понятным названием и описанием, назначьте себя исполнителем, измените статус. Откройте задачу повторно.

Описание, исполнитель и статус сохранены.

Шаг 20. Проверьте приглашение сотрудника #

Заполните SMTP в настройках установки по данным почтового провайдера. Пригласите вторую тестовую учётную запись в workspace и выберите обычные права.

Приглашение доставлено; участник видит только доступные ему workspace и проект.

Шаг 21. Проверьте вложение #

Загрузите небольшой тестовый файл в задачу и откройте его со второй учётной записи. Это проверяет объектное хранилище и доступ, а не только базу задач.

Файл скачивается после повторного открытия задачи.

Шаг 22. Остановите приложение для штатной копии #

Предупредите пользователей о перерыве. Выполняйте управляющие команды из /opt/plane-selfhost.

Остановите приложение для штатной копии
cd /opt/plane-selfhost
sudo ./setup.sh stop

Сервисы остановлены без удаления томов.

Шаг 23. Запустите штатный backup #

setup.sh backup создаёт копию по правилам выбранного комплекта. Сохраните путь результата из вывода. Перенесите копию в закрытое внешнее хранилище; restore.sh сначала проверяйте на отдельной машине.

Штатный backup сохраняет данные контейнеров. Отдельно архивируйте setup.sh, restore.sh, plane-app/plane.env, plane-app/docker-compose.yaml, plane-app/nginx-router.conf, /etc/nginx/sites-available/plane и /etc/letsencrypt с sudo в закрытое хранилище: именно они воспроизводят выбранную схему Nginx.

Запустите штатный backup
sudo ./setup.sh backup

Копия создана без ошибки и включает необходимые части установки.

Шаг 24. Возобновите работу и проверьте копию #

Снова запустите Plane. Проверьте тестовую задачу и вложение. При пробном восстановлении эти же данные должны вернуться; только наличие архива этого не подтверждает.

Возобновите работу и проверьте копию
sudo ./setup.sh start

Проект доступен после запуска и после тестового восстановления.

Если результат шага отличается #

Вернитесь к проверке того шага, на котором появилась ошибка. Первая ошибка в выводе обычно полезнее последних строк: последующие команды могли не получить нужный файл или работающую службу. Не повторяйте установку целиком поверх частично настроенного приложения.

Если страница не открывается, сначала проверьте домен и входящие порты, затем состояние приложения и его журнал. Если сайт открывается, но отдельное действие не работает, проверьте связанные компоненты — обработчик фоновых задач, почту или права пользователя. Официальные источники в конце статьи относятся к выбранному способу установки.

setup.sh upgrade скачивает новый официальный Compose и может вернуть прежний proxy. До обновления сохраните свои файлы, прочитайте изменения релиза и повторите замену proxy и маршрутов на тестовой копии. Не запускайте обновлённый комплект, пока его внешний прокси снова занимает 80/443. После восстановления подключите сайт Nginx в sites-enabled и проверьте nginx -t и продление Certbot.

После изменения nginx-router.conf либо пересоздания контейнера API или frontend перезапустите внутренний proxy: sudo docker compose -f plane-app/docker-compose.yaml --env-file plane-app/plane.env restart proxy из /opt/plane-selfhost. Nginx перечитает файл и заново разрешит адреса сервисов Docker. Затем повторите проверку проекта, вложения и совместного редактора.

Обсудить установку и настройку

Материал был полезен?

Сообщить об ошибке или задать вопрос редакции

Сообщение увидит редакция. Не указывайте пароли и личные данные. Текст удаляется через 180 дней.

О материале

ИнфраструктураРуководствоЗадача: установитьУровень: практикаPlane

Последнее обновление:

Обсуждение

Комментарии проходят подтверждение адреса и проверку редакцией.