docker.recipes

Plane Project Management

intermediate

Open source project tracking tool like Jira/Linear.

Overview

Plane is a modern, open-source project management platform that serves as a powerful alternative to Jira and Linear. Born from the need for developer-friendly issue tracking without vendor lock-in, Plane provides agile project management with cycles, modules, views, and real-time collaboration features. The platform emphasizes speed and user experience while maintaining the depth required for complex project workflows. This stack combines Plane's three-tier architecture with robust data infrastructure. The plane-web frontend delivers a responsive React-based interface, while plane-api handles business logic and REST endpoints. The plane-worker processes background tasks through Celery queues. PostgreSQL stores structured project data with full ACID compliance for data integrity, Redis provides sub-millisecond caching and task queue management, and MinIO offers S3-compatible object storage for file attachments and assets. Development teams, startups, and organizations seeking project management control without SaaS limitations will find this configuration valuable. Unlike hosted solutions, this self-hosted approach eliminates per-user costs, provides complete data ownership, and allows custom integrations. The combination is particularly suited for teams already comfortable with containerized infrastructure who need enterprise-grade project tracking with the flexibility of open-source tooling.

Key Features

  • Agile project management with sprints (cycles), feature groupings (modules), and custom issue views
  • Real-time collaborative editing and commenting powered by Redis pub/sub messaging
  • Advanced PostgreSQL-backed analytics with custom fields, filters, and project insights
  • S3-compatible file attachment system using MinIO for images, documents, and project assets
  • Celery-based background processing for notifications, imports, and scheduled tasks
  • Multi-workspace support with team permissions and project-level access controls
  • API-first architecture enabling custom integrations and automation workflows
  • GitHub, GitLab, and other VCS integrations for linking commits to issues

Common Use Cases

  • 1Software development teams transitioning from Jira to avoid licensing costs and vendor lock-in
  • 2Startups requiring enterprise project management features without per-seat pricing constraints
  • 3Organizations with data sovereignty requirements needing on-premises issue tracking
  • 4Development agencies managing multiple client projects with workspace isolation
  • 5Open source projects needing professional project management tools with community hosting
  • 6Companies requiring custom project workflows and integrations not available in SaaS solutions
  • 7Teams in regulated industries needing complete audit trails and data control for compliance

Prerequisites

  • Docker Engine 20.10+ and Docker Compose V2 for container orchestration support
  • Minimum 4GB RAM (2GB for PostgreSQL/MinIO, 1GB for Plane services, 512MB for Redis)
  • Available ports 3000 (web interface), 8000 (API), and 9001 (MinIO console)
  • Basic understanding of environment variable configuration for secrets management
  • Familiarity with PostgreSQL concepts for database maintenance and backups
  • Knowledge of object storage concepts for file upload and MinIO bucket management

For development & testing. Review security settings, change default credentials, and test thoroughly before production use. See Terms

docker-compose.yml

docker-compose.yml
1services:
2 plane-web:
3 image: makeplane/plane-frontend:latest
4 ports:
5 - "3000:3000"
6 environment:
7 NEXT_PUBLIC_API_BASE_URL: http://localhost:8000
8 depends_on:
9 - plane-api
10 networks:
11 - plane-net
12 restart: unless-stopped
13
14 plane-api:
15 image: makeplane/plane-backend:latest
16 ports:
17 - "8000:8000"
18 environment:
19 DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
20 REDIS_URL: redis://redis:6379
21 SECRET_KEY: ${SECRET_KEY}
22 AWS_ACCESS_KEY_ID: ${MINIO_ACCESS_KEY}
23 AWS_SECRET_ACCESS_KEY: ${MINIO_SECRET_KEY}
24 AWS_S3_BUCKET_NAME: uploads
25 AWS_S3_ENDPOINT_URL: http://minio:9000
26 depends_on:
27 postgres:
28 condition: service_healthy
29 redis:
30 condition: service_started
31 networks:
32 - plane-net
33 restart: unless-stopped
34
35 plane-worker:
36 image: makeplane/plane-backend:latest
37 command: celery -A plane worker -l info
38 environment:
39 DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
40 REDIS_URL: redis://redis:6379
41 SECRET_KEY: ${SECRET_KEY}
42 depends_on:
43 - plane-api
44 networks:
45 - plane-net
46 restart: unless-stopped
47
48 postgres:
49 image: postgres:16-alpine
50 environment:
51 POSTGRES_USER: ${POSTGRES_USER}
52 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
53 POSTGRES_DB: ${POSTGRES_DB}
54 volumes:
55 - postgres_data:/var/lib/postgresql/data
56 healthcheck:
57 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
58 interval: 10s
59 timeout: 5s
60 retries: 5
61 networks:
62 - plane-net
63 restart: unless-stopped
64
65 redis:
66 image: redis:7-alpine
67 volumes:
68 - redis_data:/data
69 networks:
70 - plane-net
71 restart: unless-stopped
72
73 minio:
74 image: minio/minio:latest
75 ports:
76 - "9001:9001"
77 environment:
78 MINIO_ROOT_USER: ${MINIO_ACCESS_KEY}
79 MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY}
80 volumes:
81 - minio_data:/data
82 command: server /data --console-address ":9001"
83 networks:
84 - plane-net
85 restart: unless-stopped
86
87volumes:
88 postgres_data:
89 redis_data:
90 minio_data:
91
92networks:
93 plane-net:
94 driver: bridge

.env Template

.env
1# PostgreSQL
2POSTGRES_USER=plane
3POSTGRES_PASSWORD=secure_postgres_password
4POSTGRES_DB=plane
5
6# Plane
7SECRET_KEY=$(openssl rand -hex 32)
8
9# MinIO
10MINIO_ACCESS_KEY=plane
11MINIO_SECRET_KEY=secure_minio_password

Usage Notes

  1. 1Plane at http://localhost:3000
  2. 2API at http://localhost:8000
  3. 3Modern issue tracking UI
  4. 4Cycles, modules, and views

Individual Services(6 services)

Copy individual services to mix and match with your existing compose files.

plane-web
plane-web:
  image: makeplane/plane-frontend:latest
  ports:
    - "3000:3000"
  environment:
    NEXT_PUBLIC_API_BASE_URL: http://localhost:8000
  depends_on:
    - plane-api
  networks:
    - plane-net
  restart: unless-stopped
plane-api
plane-api:
  image: makeplane/plane-backend:latest
  ports:
    - "8000:8000"
  environment:
    DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
    REDIS_URL: redis://redis:6379
    SECRET_KEY: ${SECRET_KEY}
    AWS_ACCESS_KEY_ID: ${MINIO_ACCESS_KEY}
    AWS_SECRET_ACCESS_KEY: ${MINIO_SECRET_KEY}
    AWS_S3_BUCKET_NAME: uploads
    AWS_S3_ENDPOINT_URL: http://minio:9000
  depends_on:
    postgres:
      condition: service_healthy
    redis:
      condition: service_started
  networks:
    - plane-net
  restart: unless-stopped
plane-worker
plane-worker:
  image: makeplane/plane-backend:latest
  command: celery -A plane worker -l info
  environment:
    DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
    REDIS_URL: redis://redis:6379
    SECRET_KEY: ${SECRET_KEY}
  depends_on:
    - plane-api
  networks:
    - plane-net
  restart: unless-stopped
postgres
postgres:
  image: postgres:16-alpine
  environment:
    POSTGRES_USER: ${POSTGRES_USER}
    POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    POSTGRES_DB: ${POSTGRES_DB}
  volumes:
    - postgres_data:/var/lib/postgresql/data
  healthcheck:
    test:
      - CMD-SHELL
      - pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}
    interval: 10s
    timeout: 5s
    retries: 5
  networks:
    - plane-net
  restart: unless-stopped
redis
redis:
  image: redis:7-alpine
  volumes:
    - redis_data:/data
  networks:
    - plane-net
  restart: unless-stopped
minio
minio:
  image: minio/minio:latest
  ports:
    - "9001:9001"
  environment:
    MINIO_ROOT_USER: ${MINIO_ACCESS_KEY}
    MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY}
  volumes:
    - minio_data:/data
  command: server /data --console-address ":9001"
  networks:
    - plane-net
  restart: unless-stopped

Quick Start

terminal
1# 1. Create the compose file
2cat > docker-compose.yml << 'EOF'
3services:
4 plane-web:
5 image: makeplane/plane-frontend:latest
6 ports:
7 - "3000:3000"
8 environment:
9 NEXT_PUBLIC_API_BASE_URL: http://localhost:8000
10 depends_on:
11 - plane-api
12 networks:
13 - plane-net
14 restart: unless-stopped
15
16 plane-api:
17 image: makeplane/plane-backend:latest
18 ports:
19 - "8000:8000"
20 environment:
21 DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
22 REDIS_URL: redis://redis:6379
23 SECRET_KEY: ${SECRET_KEY}
24 AWS_ACCESS_KEY_ID: ${MINIO_ACCESS_KEY}
25 AWS_SECRET_ACCESS_KEY: ${MINIO_SECRET_KEY}
26 AWS_S3_BUCKET_NAME: uploads
27 AWS_S3_ENDPOINT_URL: http://minio:9000
28 depends_on:
29 postgres:
30 condition: service_healthy
31 redis:
32 condition: service_started
33 networks:
34 - plane-net
35 restart: unless-stopped
36
37 plane-worker:
38 image: makeplane/plane-backend:latest
39 command: celery -A plane worker -l info
40 environment:
41 DATABASE_URL: postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}
42 REDIS_URL: redis://redis:6379
43 SECRET_KEY: ${SECRET_KEY}
44 depends_on:
45 - plane-api
46 networks:
47 - plane-net
48 restart: unless-stopped
49
50 postgres:
51 image: postgres:16-alpine
52 environment:
53 POSTGRES_USER: ${POSTGRES_USER}
54 POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
55 POSTGRES_DB: ${POSTGRES_DB}
56 volumes:
57 - postgres_data:/var/lib/postgresql/data
58 healthcheck:
59 test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
60 interval: 10s
61 timeout: 5s
62 retries: 5
63 networks:
64 - plane-net
65 restart: unless-stopped
66
67 redis:
68 image: redis:7-alpine
69 volumes:
70 - redis_data:/data
71 networks:
72 - plane-net
73 restart: unless-stopped
74
75 minio:
76 image: minio/minio:latest
77 ports:
78 - "9001:9001"
79 environment:
80 MINIO_ROOT_USER: ${MINIO_ACCESS_KEY}
81 MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY}
82 volumes:
83 - minio_data:/data
84 command: server /data --console-address ":9001"
85 networks:
86 - plane-net
87 restart: unless-stopped
88
89volumes:
90 postgres_data:
91 redis_data:
92 minio_data:
93
94networks:
95 plane-net:
96 driver: bridge
97EOF
98
99# 2. Create the .env file
100cat > .env << 'EOF'
101# PostgreSQL
102POSTGRES_USER=plane
103POSTGRES_PASSWORD=secure_postgres_password
104POSTGRES_DB=plane
105
106# Plane
107SECRET_KEY=$(openssl rand -hex 32)
108
109# MinIO
110MINIO_ACCESS_KEY=plane
111MINIO_SECRET_KEY=secure_minio_password
112EOF
113
114# 3. Start the services
115docker compose up -d
116
117# 4. View logs
118docker compose logs -f

One-Liner

Run this command to download and set up the recipe in one step:

terminal
1curl -fsSL https://docker.recipes/api/recipes/plane-project/run | bash

Troubleshooting

  • plane-api fails to connect to PostgreSQL: Ensure PostgreSQL health check passes before API startup and verify DATABASE_URL environment variable format
  • File uploads return 403 errors: Check MinIO bucket creation and verify AWS_S3_BUCKET_NAME exists with proper access policies configured
  • Background tasks not processing: Verify plane-worker container is running and Redis connection is established for Celery task queue
  • Frontend shows API connection errors: Confirm NEXT_PUBLIC_API_BASE_URL matches your domain and plane-api is accessible on port 8000
  • PostgreSQL connection pool exhausted: Increase max_connections in PostgreSQL config or implement connection pooling like PgBouncer for high-traffic instances
  • MinIO console inaccessible: Verify MINIO_ROOT_USER and MINIO_ROOT_PASSWORD are set correctly and console is bound to port 9001

Community Notes

Loading...
Loading notes...

Download Recipe Kit

Get all files in a ready-to-deploy package

Includes docker-compose.yml, .env template, README, and license

Ad Space