Plane is a modern, open-source project management tool built for agile teams. In this guide, you’ll install the Plane Community Edition on a bare-metal Ubuntu 24.04 server, run it in Docker, and expose it securely through HAProxy 3.2 with SSL termination using Let’s Encrypt DNS challenge via Cloudflare.
This setup keeps Plane running over HTTP internally and handles TLS externally with HAProxy, while not requiring port 80 to be open publicly.
Prerequisites
- Ubuntu 24.04 server
- Domain name:
plane.maksonlee.com
, managed in Cloudflare - Public port 443 open (port 80 is not required)
- Docker + Compose plugin installed: Install Docker on Ubuntu 24.04
Install required packages:
sudo apt update
sudo apt install -y certbot python3-certbot-dns-cloudflare
sudo add-apt-repository ppa:vbernat/haproxy-3.2 -y
sudo apt-get install haproxy=3.2.\*
sudo systemctl enable --now haproxy
- Install Plane Community Edition
cd ~
mkdir plane-selfhost
cd plane-selfhost
curl -fsSL -o setup.sh https://github.com/makeplane/plane/releases/latest/download/setup.sh
chmod a+x setup.sh
./setup.sh install
cd ~
sudo mv plane-selfhost /opt/
- Configure Plane Environment for HTTPS Awareness
Edit the generated plane.env
file:
vi /opt/plane-selfhost/plane-app/plane.env
Update the following values:
APP_DOMAIN=plane.maksonlee.com
WEB_URL=https://plane.maksonlee.com
CORS_ALLOWED_ORIGINS=https://plane.maksonlee.com
- Issue HTTPS Certificate via Cloudflare DNS
Create Cloudflare credentials:
mkdir -p ~/.secrets/certbot
vi ~/.secrets/certbot/cloudflare.ini
Add:
dns_cloudflare_api_token = YOUR_CLOUDFLARE_API_TOKEN
Secure the file:
chmod 600 ~/.secrets/certbot/cloudflare.ini
Request certificate:
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials ~/.secrets/certbot/cloudflare.ini \
-d plane.maksonlee.com
- Bundle Certificate for HAProxy
sudo mkdir -p /etc/haproxy/certs/
sudo bash -c "cat /etc/letsencrypt/live/plane.maksonlee.com/fullchain.pem \
/etc/letsencrypt/live/plane.maksonlee.com/privkey.pem \
> /etc/haproxy/certs/plane.maksonlee.com.pem"
sudo chmod 600 /etc/haproxy/certs/plane.maksonlee.com.pem
- Configure HAProxy for SSL Termination
Edit /etc/haproxy/haproxy.cfg
:
frontend https_in
bind *:443 ssl crt /etc/haproxy/certs/ alpn h2,http/1.1
mode http
default_backend plane
backend plane
mode http
option http-buffer-request
option http-keep-alive
option forwardfor
http-request set-header X-Forwarded-Proto https
server plane1 127.0.0.1:80
Restart HAProxy:
sudo systemctl restart haproxy
- Automate Certificate Renewal
Create deploy hook to reload HAProxy on renewal:
sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-haproxy.sh > /dev/null <<EOF
#!/bin/bash
cat /etc/letsencrypt/live/plane.maksonlee.com/fullchain.pem \
/etc/letsencrypt/live/plane.maksonlee.com/privkey.pem \
> /etc/haproxy/certs/plane.maksonlee.com.pem
systemctl reload haproxy
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-haproxy.sh
Test renewal:
sudo certbot renew --dry-run
- Create a systemd Service for Plane
Create the /etc/systemd/system/plane.service service file:
[Unit]
Description=Plane
After=network-online.target docker.service
Requires=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/plane-selfhost
ExecStart=/opt/plane-selfhost/setup.sh start
ExecStop=/opt/plane-selfhost/setup.sh stop
TimeoutStartSec=0
[Install]
WantedBy=multi-user.target
Enable and start it:
sudo systemctl daemon-reexec
sudo systemctl daemon-reload
sudo systemctl enable --now plane
- Access Plane
Visit https://plane.maksonlee.com and you’ll see the setup UI:
Welcome Screen

Setup Admin

Login

Onboarding



