How to Self-Host Plane on a VPS: Open-Source Project Management for Engineering Teams
Plane is an open-source project management tool built for engineering teams — it provides issue tracking, sprints (Cycles), epics (Modules), roadmaps, pages (wiki), analytics, and GitHub integration. Plane targets the same market as Linear ($8/user/month) and Jira ($8.15/user/month). Self-hosting Plane on a VPS eliminates per-seat licensing for teams of any size.
What Plane Provides
- Issues: Create, assign, prioritize, and track work items with custom states, labels, and priorities
- Cycles: Time-boxed sprints with burndown charts and velocity tracking
- Modules: Group issues into epics or feature areas with progress tracking
- Views: Board (Kanban), List, Spreadsheet, Gantt, and Calendar views
- Pages: Collaborative document editor for specifications, meeting notes, and wikis
- Analytics: Issue distribution by state, assignee, priority, and custom filters
- GitHub integration: Link commits and pull requests to issues
Server Requirements
- Minimum 4 GB RAM (Plane runs ~8 Docker services)
- Docker and Docker Compose
- 20+ GB NVMe storage
Step 1: Clone Plane
<code">mkdir -p /opt/plane && cd /opt/plane git clone https://github.com/makeplane/plane.git . # Copy environment template cp .env.example .env nano .env
<code"># Required changes: SECRET_KEY=your_50_char_random_key # python3 -c "import secrets; print(secrets.token_hex(25))" # Database POSTGRES_USER=plane POSTGRES_PASSWORD=StrongPlaneDbPassword! POSTGRES_DB=plane # Redis REDIS_URL=redis://plane-redis:6379/ # Application WEB_URL=https://plane.yourdomain.com CORS_ALLOWED_ORIGINS=https://plane.yourdomain.com # Email (required for invites) EMAIL_HOST=smtp.mailgun.org EMAIL_PORT=587 EMAIL_HOST_USER=postmaster@mg.yourdomain.com EMAIL_HOST_PASSWORD=your_smtp_password EMAIL_USE_TLS=1 DEFAULT_FROM_EMAIL=plane@yourdomain.com # File storage — local or S3 USE_MINIO=1 # Use built-in MinIO for S3-compatible storage MINIO_ROOT_USER=plane_minio_user MINIO_ROOT_PASSWORD=StrongMinioPassword!
Step 2: Start Plane
<code"># Plane uses its own setup script chmod +x setup.sh sudo ./setup.sh start # Or use Docker Compose directly: docker compose -f docker-compose.yml up -d # Check all services are running docker compose ps # Plane services: web, api, worker, beat (scheduler), postgres, redis, minio
Step 3: Nginx Reverse Proxy
<code">sudo nano /etc/nginx/sites-available/plane
<code">server {
listen 80;
server_name plane.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name plane.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/plane.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/plane.yourdomain.com/privkey.pem;
client_max_body_size 50M;
# Next.js frontend
location / {
proxy_pass http://127.0.0.1:3000;
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-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
}
# Django API
location /api/ {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
}
# File storage (MinIO)
location /uploads/ {
proxy_pass http://127.0.0.1:9000;
proxy_set_header Host $host;
}
}
<code">sudo ln -s /etc/nginx/sites-available/plane /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx sudo certbot --nginx -d plane.yourdomain.com
Step 4: Initial Setup
- Visit
https://plane.yourdomain.com - Create your admin account (first user)
- Create a Workspace (e.g., “Acme Engineering”)
- Create a Project → add members
- Configure states, labels, and priority levels per project
Step 5: Configure GitHub Integration
- Go to github.com/settings/apps → New GitHub App
- Homepage URL:
https://plane.yourdomain.com - Callback URL:
https://plane.yourdomain.com/auth/github/callback/ - Webhook URL:
https://plane.yourdomain.com/api/v1/integrations/github/webhook/ - Permissions: Issues (read/write), Pull requests (read/write), Metadata (read)
- Add the GitHub App ID and private key to Plane’s Settings → Integrations
Key Plane Workflows
<code"># Creating issues via API
curl -X POST https://plane.yourdomain.com/api/v1/workspaces/{slug}/projects/{id}/issues/ \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Implement user authentication",
"state": "backlog_state_id",
"priority": "high",
"assignees": ["user_id"]
}'
# Export issues to CSV
curl https://plane.yourdomain.com/api/v1/workspaces/{slug}/exports/ \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{"provider": "csv", "project": ["project_id"]}'
Updating Plane
<code">cd /opt/plane git pull sudo ./setup.sh update
Getting Started
Plane needs 4 GB RAM for its eight Docker services (API, worker, scheduler, Next.js frontend, PostgreSQL, Redis, MinIO). KVM VPS plans at VPS.DO with 4 GB RAM run Plane comfortably for teams up to 50 people. For larger teams or higher issue volumes, 8 GB RAM provides comfortable headroom for the PostgreSQL query load.
Conclusion
Self-hosted Plane provides a modern, linear-style project management experience — Kanban boards, sprints, epics, roadmaps, and GitHub integration — without $8/user/month Linear fees or $8.15/user/month Jira fees. For a 15-person engineering team, that’s $1,440/year saved against Linear. The Docker Compose deployment is maintained by the Plane team with a dedicated setup script for easy installation and updates.