docker.recipes

Plane Issue Tracker

intermediate

Open-source project tracking tool, JIRA alternative.

Overview

Plane is an open-source project management and issue tracking platform designed as a modern alternative to JIRA. Built with a focus on simplicity and speed, Plane offers comprehensive project tracking capabilities including issue management, sprint planning, roadmap visualization, and team collaboration features. The platform emphasizes user experience with a clean, intuitive interface while providing powerful features like custom workflows, multiple project views, and advanced filtering options. This Docker deployment creates a complete Plane installation using a multi-service architecture. The stack includes a Next.js frontend (web), Django REST API backend (api), Celery worker processes for background tasks (worker), Celery beat scheduler for periodic tasks (beat), PostgreSQL for primary data storage (db), Redis for caching and task queues (redis), and MinIO for S3-compatible file storage (minio). The backend services share the same Docker image but run different processes to handle web requests, background processing, and scheduled tasks separately. This configuration is ideal for teams seeking a self-hosted project management solution with enterprise-grade features but without vendor lock-in. Organizations transitioning from JIRA, development teams requiring integrated issue tracking with Git workflows, and companies needing customizable project management tools will benefit from this deployment. The separation of concerns across multiple services provides scalability while maintaining data sovereignty and customization flexibility.

Key Features

  • Comprehensive issue tracking with custom fields, labels, and workflow states
  • Sprint planning and cycle management with burndown charts and velocity tracking
  • Multiple project views including Kanban boards, lists, and calendar views
  • Advanced filtering and search capabilities across issues and projects
  • File attachment support with S3-compatible storage via MinIO integration
  • Real-time collaboration features powered by Redis pub/sub messaging
  • Background task processing for notifications, imports, and data synchronization
  • GitHub and GitLab integration for automatic issue creation and status updates

Common Use Cases

  • 1Software development teams migrating from JIRA to an open-source alternative
  • 2Startups and scale-ups needing project management without per-user licensing costs
  • 3Organizations requiring self-hosted solutions for data privacy and compliance
  • 4Development teams integrating issue tracking with existing Git workflows
  • 5Companies needing customizable project management workflows and reporting
  • 6Remote teams requiring real-time collaboration on project planning and execution
  • 7Agencies managing multiple client projects with separate workspaces and permissions

Prerequisites

  • Docker and Docker Compose installed with minimum 4GB total RAM available
  • Port 3000 available for web interface and port 9000 for MinIO console access
  • Environment variables configured including SECRET_KEY, database password, and MinIO credentials
  • SMTP server details for email notifications (optional but recommended)
  • Basic understanding of Django/Python applications for configuration and troubleshooting
  • Familiarity with PostgreSQL for database maintenance and backup procedures

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 web:
3 image: makeplane/plane-frontend:latest
4 container_name: plane-web
5 environment:
6 - NEXT_PUBLIC_API_BASE_URL=${API_URL}
7 ports:
8 - "3000:3000"
9 depends_on:
10 - api
11 networks:
12 - plane-network
13 restart: unless-stopped
14
15 api:
16 image: makeplane/plane-backend:latest
17 container_name: plane-api
18 environment:
19 - DEBUG=0
20 - DJANGO_SETTINGS_MODULE=plane.settings.production
21 - SECRET_KEY=${SECRET_KEY}
22 - DATABASE_URL=postgres://plane:${DB_PASSWORD}@db:5432/plane
23 - REDIS_URL=redis://redis:6379/
24 - EMAIL_HOST=${EMAIL_HOST}
25 - EMAIL_HOST_USER=${EMAIL_HOST_USER}
26 - EMAIL_HOST_PASSWORD=${EMAIL_HOST_PASSWORD}
27 - AWS_ACCESS_KEY_ID=${MINIO_ACCESS_KEY}
28 - AWS_SECRET_ACCESS_KEY=${MINIO_SECRET_KEY}
29 - AWS_S3_ENDPOINT_URL=${MINIO_URL}
30 - AWS_S3_BUCKET_NAME=plane
31 - FILE_SIZE_LIMIT=5242880
32 - WEB_URL=${WEB_URL}
33 depends_on:
34 - db
35 - redis
36 networks:
37 - plane-network
38 restart: unless-stopped
39
40 worker:
41 image: makeplane/plane-backend:latest
42 container_name: plane-worker
43 command: ./bin/worker
44 environment:
45 - DEBUG=0
46 - DJANGO_SETTINGS_MODULE=plane.settings.production
47 - SECRET_KEY=${SECRET_KEY}
48 - DATABASE_URL=postgres://plane:${DB_PASSWORD}@db:5432/plane
49 - REDIS_URL=redis://redis:6379/
50 depends_on:
51 - api
52 - redis
53 networks:
54 - plane-network
55 restart: unless-stopped
56
57 beat:
58 image: makeplane/plane-backend:latest
59 container_name: plane-beat
60 command: ./bin/beat
61 environment:
62 - DEBUG=0
63 - DJANGO_SETTINGS_MODULE=plane.settings.production
64 - SECRET_KEY=${SECRET_KEY}
65 - DATABASE_URL=postgres://plane:${DB_PASSWORD}@db:5432/plane
66 - REDIS_URL=redis://redis:6379/
67 depends_on:
68 - api
69 - redis
70 networks:
71 - plane-network
72 restart: unless-stopped
73
74 db:
75 image: postgres:15-alpine
76 container_name: plane-db
77 environment:
78 - POSTGRES_USER=plane
79 - POSTGRES_PASSWORD=${DB_PASSWORD}
80 - POSTGRES_DB=plane
81 volumes:
82 - postgres-data:/var/lib/postgresql/data
83 networks:
84 - plane-network
85 restart: unless-stopped
86
87 redis:
88 image: redis:7-alpine
89 container_name: plane-redis
90 volumes:
91 - redis-data:/data
92 networks:
93 - plane-network
94 restart: unless-stopped
95
96 minio:
97 image: minio/minio:latest
98 container_name: plane-minio
99 environment:
100 - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}
101 - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}
102 command: server /data --console-address ":9001"
103 volumes:
104 - minio-data:/data
105 ports:
106 - "9000:9000"
107 networks:
108 - plane-network
109 restart: unless-stopped
110
111volumes:
112 postgres-data:
113 redis-data:
114 minio-data:
115
116networks:
117 plane-network:
118 driver: bridge

.env Template

.env
1# Plane
2WEB_URL=http://localhost:3000
3API_URL=http://localhost:8000
4
5# Generate with: openssl rand -hex 32
6SECRET_KEY=your_secret_key_here
7DB_PASSWORD=secure_plane_password
8
9# MinIO
10MINIO_ACCESS_KEY=plane_minio
11MINIO_SECRET_KEY=secure_minio_password
12MINIO_URL=http://minio:9000
13
14# Email (optional)
15EMAIL_HOST=smtp.example.com
16EMAIL_HOST_USER=plane@example.com
17EMAIL_HOST_PASSWORD=email_password

Usage Notes

  1. 1Web UI at http://localhost:3000
  2. 2Create plane bucket in MinIO first
  3. 3JIRA-like issue tracking
  4. 4Supports cycles, modules, views
  5. 5GitHub and GitLab integrations

Individual Services(7 services)

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

web
web:
  image: makeplane/plane-frontend:latest
  container_name: plane-web
  environment:
    - NEXT_PUBLIC_API_BASE_URL=${API_URL}
  ports:
    - "3000:3000"
  depends_on:
    - api
  networks:
    - plane-network
  restart: unless-stopped
api
api:
  image: makeplane/plane-backend:latest
  container_name: plane-api
  environment:
    - DEBUG=0
    - DJANGO_SETTINGS_MODULE=plane.settings.production
    - SECRET_KEY=${SECRET_KEY}
    - DATABASE_URL=postgres://plane:${DB_PASSWORD}@db:5432/plane
    - REDIS_URL=redis://redis:6379/
    - EMAIL_HOST=${EMAIL_HOST}
    - EMAIL_HOST_USER=${EMAIL_HOST_USER}
    - EMAIL_HOST_PASSWORD=${EMAIL_HOST_PASSWORD}
    - AWS_ACCESS_KEY_ID=${MINIO_ACCESS_KEY}
    - AWS_SECRET_ACCESS_KEY=${MINIO_SECRET_KEY}
    - AWS_S3_ENDPOINT_URL=${MINIO_URL}
    - AWS_S3_BUCKET_NAME=plane
    - FILE_SIZE_LIMIT=5242880
    - WEB_URL=${WEB_URL}
  depends_on:
    - db
    - redis
  networks:
    - plane-network
  restart: unless-stopped
worker
worker:
  image: makeplane/plane-backend:latest
  container_name: plane-worker
  command: ./bin/worker
  environment:
    - DEBUG=0
    - DJANGO_SETTINGS_MODULE=plane.settings.production
    - SECRET_KEY=${SECRET_KEY}
    - DATABASE_URL=postgres://plane:${DB_PASSWORD}@db:5432/plane
    - REDIS_URL=redis://redis:6379/
  depends_on:
    - api
    - redis
  networks:
    - plane-network
  restart: unless-stopped
beat
beat:
  image: makeplane/plane-backend:latest
  container_name: plane-beat
  command: ./bin/beat
  environment:
    - DEBUG=0
    - DJANGO_SETTINGS_MODULE=plane.settings.production
    - SECRET_KEY=${SECRET_KEY}
    - DATABASE_URL=postgres://plane:${DB_PASSWORD}@db:5432/plane
    - REDIS_URL=redis://redis:6379/
  depends_on:
    - api
    - redis
  networks:
    - plane-network
  restart: unless-stopped
db
db:
  image: postgres:15-alpine
  container_name: plane-db
  environment:
    - POSTGRES_USER=plane
    - POSTGRES_PASSWORD=${DB_PASSWORD}
    - POSTGRES_DB=plane
  volumes:
    - postgres-data:/var/lib/postgresql/data
  networks:
    - plane-network
  restart: unless-stopped
redis
redis:
  image: redis:7-alpine
  container_name: plane-redis
  volumes:
    - redis-data:/data
  networks:
    - plane-network
  restart: unless-stopped
minio
minio:
  image: minio/minio:latest
  container_name: plane-minio
  environment:
    - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}
    - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}
  command: server /data --console-address ":9001"
  volumes:
    - minio-data:/data
  ports:
    - "9000:9000"
  networks:
    - plane-network
  restart: unless-stopped

Quick Start

terminal
1# 1. Create the compose file
2cat > docker-compose.yml << 'EOF'
3services:
4 web:
5 image: makeplane/plane-frontend:latest
6 container_name: plane-web
7 environment:
8 - NEXT_PUBLIC_API_BASE_URL=${API_URL}
9 ports:
10 - "3000:3000"
11 depends_on:
12 - api
13 networks:
14 - plane-network
15 restart: unless-stopped
16
17 api:
18 image: makeplane/plane-backend:latest
19 container_name: plane-api
20 environment:
21 - DEBUG=0
22 - DJANGO_SETTINGS_MODULE=plane.settings.production
23 - SECRET_KEY=${SECRET_KEY}
24 - DATABASE_URL=postgres://plane:${DB_PASSWORD}@db:5432/plane
25 - REDIS_URL=redis://redis:6379/
26 - EMAIL_HOST=${EMAIL_HOST}
27 - EMAIL_HOST_USER=${EMAIL_HOST_USER}
28 - EMAIL_HOST_PASSWORD=${EMAIL_HOST_PASSWORD}
29 - AWS_ACCESS_KEY_ID=${MINIO_ACCESS_KEY}
30 - AWS_SECRET_ACCESS_KEY=${MINIO_SECRET_KEY}
31 - AWS_S3_ENDPOINT_URL=${MINIO_URL}
32 - AWS_S3_BUCKET_NAME=plane
33 - FILE_SIZE_LIMIT=5242880
34 - WEB_URL=${WEB_URL}
35 depends_on:
36 - db
37 - redis
38 networks:
39 - plane-network
40 restart: unless-stopped
41
42 worker:
43 image: makeplane/plane-backend:latest
44 container_name: plane-worker
45 command: ./bin/worker
46 environment:
47 - DEBUG=0
48 - DJANGO_SETTINGS_MODULE=plane.settings.production
49 - SECRET_KEY=${SECRET_KEY}
50 - DATABASE_URL=postgres://plane:${DB_PASSWORD}@db:5432/plane
51 - REDIS_URL=redis://redis:6379/
52 depends_on:
53 - api
54 - redis
55 networks:
56 - plane-network
57 restart: unless-stopped
58
59 beat:
60 image: makeplane/plane-backend:latest
61 container_name: plane-beat
62 command: ./bin/beat
63 environment:
64 - DEBUG=0
65 - DJANGO_SETTINGS_MODULE=plane.settings.production
66 - SECRET_KEY=${SECRET_KEY}
67 - DATABASE_URL=postgres://plane:${DB_PASSWORD}@db:5432/plane
68 - REDIS_URL=redis://redis:6379/
69 depends_on:
70 - api
71 - redis
72 networks:
73 - plane-network
74 restart: unless-stopped
75
76 db:
77 image: postgres:15-alpine
78 container_name: plane-db
79 environment:
80 - POSTGRES_USER=plane
81 - POSTGRES_PASSWORD=${DB_PASSWORD}
82 - POSTGRES_DB=plane
83 volumes:
84 - postgres-data:/var/lib/postgresql/data
85 networks:
86 - plane-network
87 restart: unless-stopped
88
89 redis:
90 image: redis:7-alpine
91 container_name: plane-redis
92 volumes:
93 - redis-data:/data
94 networks:
95 - plane-network
96 restart: unless-stopped
97
98 minio:
99 image: minio/minio:latest
100 container_name: plane-minio
101 environment:
102 - MINIO_ROOT_USER=${MINIO_ACCESS_KEY}
103 - MINIO_ROOT_PASSWORD=${MINIO_SECRET_KEY}
104 command: server /data --console-address ":9001"
105 volumes:
106 - minio-data:/data
107 ports:
108 - "9000:9000"
109 networks:
110 - plane-network
111 restart: unless-stopped
112
113volumes:
114 postgres-data:
115 redis-data:
116 minio-data:
117
118networks:
119 plane-network:
120 driver: bridge
121EOF
122
123# 2. Create the .env file
124cat > .env << 'EOF'
125# Plane
126WEB_URL=http://localhost:3000
127API_URL=http://localhost:8000
128
129# Generate with: openssl rand -hex 32
130SECRET_KEY=your_secret_key_here
131DB_PASSWORD=secure_plane_password
132
133# MinIO
134MINIO_ACCESS_KEY=plane_minio
135MINIO_SECRET_KEY=secure_minio_password
136MINIO_URL=http://minio:9000
137
138# Email (optional)
139EMAIL_HOST=smtp.example.com
140EMAIL_HOST_USER=plane@example.com
141EMAIL_HOST_PASSWORD=email_password
142EOF
143
144# 3. Start the services
145docker compose up -d
146
147# 4. View logs
148docker 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-pm/run | bash

Troubleshooting

  • plane-api container fails to start: Verify DATABASE_URL format and ensure plane-db is fully initialized before API startup
  • File uploads not working: Check MinIO credentials match between plane-api AWS_* variables and plane-minio MINIO_ROOT_* settings
  • Background tasks not processing: Ensure plane-worker container is running and Redis connection is established via REDIS_URL
  • Email notifications not sending: Verify EMAIL_HOST, EMAIL_HOST_USER, and EMAIL_HOST_PASSWORD variables are correctly set in plane-api
  • plane-web shows API connection errors: Confirm NEXT_PUBLIC_API_BASE_URL points to accessible plane-api service and matches your domain setup
  • Scheduled tasks not executing: Check plane-beat container logs and ensure Celery beat scheduler is connecting to Redis successfully

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