plane Documentation

Open-source Jira, Linear, Monday, and ClickUp alternative. Plane is a modern project management platform to manage tasks, sprints, docs, and triage.

55,890 stars TypeScript TypeScript 1 file Β· ~3,022 tokens #boards#bug-tracker#django#docker#gantt#issue-tracker#jira#jira-alternative
RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy plane on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name plane -p 6379:6379 --restart always valkey/valkey:7.2.11-alpine
Configured Services & Ports
βš™οΈ plane-redis
image: valkey/valkey:7.2.11-alpine
πŸ”Œ Ports: 6379:6379
βš™οΈ plane-mq
image: rabbitmq:3.13.6-management-alpine
βš™οΈ plane-minio
image: docker.io/pgsty/silo:RELEASE.2026-09-16T00-00-00Z
πŸ”Œ Ports: 9000:9000, 9090:9090
βš™οΈ plane-db
image: postgres:15.7-alpine
πŸ”Œ Ports: 5432:5432
βš™οΈ api
image: build locally
πŸ”Œ Ports: 8000:8000
βš™οΈ worker
image: build locally
βš™οΈ beat-worker
image: build locally
βš™οΈ migrator
image: build locally
docker-compose.yml
yaml (docker-compose.yml)
services:
  plane-redis:
    image: valkey/valkey:7.2.11-alpine
    restart: unless-stopped
    networks:
      - dev_env
    volumes:
      - redisdata:/data
    ports:
      - "6379:6379"

  plane-mq:
    image: rabbitmq:3.13.6-management-alpine
    restart: unless-stopped
    networks:
      - dev_env
    volumes:
      - rabbitmq_data:/var/lib/rabbitmq
    env_file:
      - .env
    environment:
      RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER}
      RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD}
      RABBITMQ_DEFAULT_VHOST: ${RABBITMQ_VHOST}

  plane-minio:
    image: docker.io/pgsty/silo:RELEASE.2026-09-16T00-00-00Z
    restart: unless-stopped
    networks:
      - dev_env
    entrypoint: >
      /bin/sh -c "
      mkdir -p /export/${AWS_S3_BUCKET_NAME} &&
      silo server /export --console-address ':9090' &
      sleep 5 &&
      mc alias set myminio http://localhost:9000 ${AWS_ACCESS_KEY_ID} ${AWS_SECRET_ACCESS_KEY} &&
      mc mb myminio/${AWS_S3_BUCKET_NAME} -p || true
      && tail -f /dev/null
      "
    volumes:
      - uploads:/export
    env_file:
      - .env
    environment:
      MINIO_ROOT_USER: ${AWS_ACCESS_KEY_ID}
      MINIO_ROOT_PASSWORD: ${AWS_SECRET_ACCESS_KEY}
    ports:
      - "9000:9000"
      - "9090:9090"

  plane-db:
    image: postgres:15.7-alpine
    restart: unless-stopped
    networks:
      - dev_env
    command: postgres -c 'max_connections=1000'
    volumes:
      - pgdata:/var/lib/postgresql/data
    env_file:
      - .env
    environment:
      PGDATA: /var/lib/postgresql/data
    ports:
      - "5432:5432"

  api:
    build:
      context: ./apps/api
      dockerfile: Dockerfile.dev
      args:
        DOCKER_BUILDKIT: 1
    restart: unless-stopped
    networks:
      - dev_env
    volumes:
      - ./apps/api:/code
    command: ./bin/docker-entrypoint-api-local.sh
    env_file:
      - ./apps/api/.env
    depends_on:
      - plane-db
      - plane-redis
      - plane-mq
    ports:
      - "8000:8000"

  worker:
    build:
      context: ./apps/api
      dockerfile: Dockerfile.dev
      args:
        DOCKER_BUILDKIT: 1
    restart: unless-stopped
    networks:
      - dev_env
    volumes:
      - ./apps/api:/code
    command: ./bin/docker-entrypoint-worker.sh
    env_file:
      - ./apps/api/.env
    depends_on:
      - api
      - plane-db
      - plane-redis

  beat-worker:
    build:
      context: ./apps/api
      dockerfile: Dockerfile.dev
      args:
        DOCKER_BUILDKIT: 1
    restart: unless-stopped
    networks:
      - dev_env
    volumes:
      - ./apps/api:/code
    command: ./bin/docker-entrypoint-beat.sh
    env_file:
      - ./apps/api/.env
    depends_on:
      - api
      - plane-db
      - plane-redis

  migrator:
    build:
      context: ./apps/api
      dockerfile: Dockerfile.dev
      args:
        DOCKER_BUILDKIT: 1
    restart: "no"
    networks:
      - dev_env
    volumes:
      - ./apps/api:/code
    command: ./bin/docker-entrypoint-migrator.sh --settings=plane.settings.local
    env_file:
      - ./apps/api/.env
    depends_on:
      - plane-db
      - plane-redis

volumes:
  redisdata:
  uploads:
  pgdata:
  rabbitmq_data:

networks:
  dev_env:
    driver: bridge

CONTRIBUTING

Contributing to Plane

Thank you for showing an interest in contributing to Plane! All kinds of contributions are valuable to us. In this guide, we will cover how you can quickly onboard and make your first contribution.

Submitting an issue

Before submitting a new issue, please search the issues tab. Maybe an issue or discussion already exists and might inform you of workarounds. Otherwise, you can give new information.

While we want to fix all the issues, before fixing a bug we need to be able to reproduce and confirm it. Please provide us with a minimal reproduction scenario using a repository or Gist. Having a live, reproducible scenario gives us the information without asking questions back & forth with additional questions like:

  • 3rd-party libraries being used and their versions
  • a use-case that fails

Without said minimal reproduction, we won't be able to investigate all issues, and the issue might not be resolved.

You can open a new issue with this issue form.

Naming conventions for issues

When opening a new issue, please use a clear and concise title that follows this format:

  • For bugs: πŸ› Bug: [short description]
  • For features: πŸš€ Feature: [short description]
  • For improvements: πŸ› οΈ Improvement: [short description]
  • For documentation: πŸ“˜ Docs: [short description]

Examples:

  • πŸ› Bug: API token expiry time not saving correctly
  • πŸ“˜ Docs: Clarify RAM requirement for local setup
  • πŸš€ Feature: Allow custom time selection for token expiration

This helps us triage and manage issues more efficiently.

Projects setup and Architecture

Requirements

  • Docker Engine installed and running
  • Node.js version 20+ LTS version
  • Python version 3.8+
  • Postgres version v14
  • Redis version v6.2.7
  • Memory: Minimum 12 GB RAM recommended

    ⚠️ Running the project on a system with only 8 GB RAM may lead to setup failures or memory crashes (especially during Docker container build/start or dependency install). Use cloud environments like GitHub Codespaces or upgrade local RAM if possible.

Setup the project

The project is a monorepo, with backend api and frontend in a single repo.

The backend is a django project which is kept inside apps/api

  1. Clone the repo
bash
git clone https://github.com/makeplane/plane.git [folder-name]
cd [folder-name]
chmod +x setup.sh
  1. Run setup.sh
bash
./setup.sh
  1. Start the containers
bash
docker compose -f docker-compose-local.yml up
  1. Start web apps:
bash
pnpm dev
  1. Open your browser to http://localhost:3001/god-mode/ and register yourself as instance admin
  2. Open up your browser to http://localhost:3000 then log in using the same credentials from the previous step

That’s it! You’re all set to begin coding. Remember to refresh your browser if changes don’t auto-reload. Happy contributing! πŸŽ‰

Missing a Feature?

If a feature is missing, you can directly request a new one here. You also can do the same by choosing "πŸš€ Feature" when raising a New Issue on our GitHub Repository.
If you would like to implement it, an issue with your proposal must be submitted first, to be sure that we can use it. Please consider the guidelines given below.

Coding guidelines

To ensure consistency throughout the source code, please keep these rules in mind as you are working:

  • All features or bug fixes must be tested by one or more specs (unit-tests).
  • We lint with OxLint using the shared .oxlintrc.json and format with oxfmt using .oxfmtrc.json.

Ways to contribute

  • Try Plane Cloud and the self hosting platform and give feedback
  • Add new integrations
  • Add or update translations
  • Help with open issues or create your own
  • Share your thoughts and suggestions with us
  • Help create tutorials and blog posts
  • Request a feature by submitting a proposal
  • Report a bug
  • Improve documentation - fix incomplete or missing docs, bad wording, examples or explanations.

Contributing to language support

This guide is designed to help contributors understand how to add or update translations in the application.

Understanding translation structure

File organization

Translations are organized by language in the locales directory. Each language has its own folder containing JSON files for translations. Here's how it looks:

text
packages/i18n/src/locales/
    β”œβ”€β”€ en/
    β”‚   β”œβ”€β”€ core.json       # Critical translations
    β”‚   └── translations.json
    β”œβ”€β”€ fr/
    β”‚   └── translations.json
    └── [language]/
        └── translations.json

Nested structure

To keep translations organized, we use a nested structure for keys. This makes it easier to manage and locate specific translations. For example:

json
{
  "issue": {
    "label": "Work item",
    "title": {
      "label": "Work item title"
    }
  }
}

Translation formatting guide

We use IntlMessageFormat to handle dynamic content, such as variables and pluralization. Here's how to format your translations:

Examples

  • Simple variables

    json
    {
      "greeting": "Hello, {name}!"
    }
  • Pluralization

    json
    {
      "items": "{count, plural, one {Work item} other {Work items}}"
    }

Contributing guidelines

Updating existing translations

  1. Locate the key in locales/<language>/translations.json.

  2. Update the value while ensuring the key structure remains intact.

  3. Preserve any existing ICU formats (e.g., variables, pluralization).

Adding new translation keys

  1. When introducing a new key, ensure it is added to all language files, even if translations are not immediately available. Use English as a placeholder if needed.

  2. Keep the nesting structure consistent across all languages.

  3. If the new key requires dynamic content (e.g., variables or pluralization), ensure the ICU format is applied uniformly across all languages.

Adding new languages

Adding a new language involves several steps to ensure it integrates seamlessly with the project. Follow these instructions carefully:

  1. Update type definitions
    Add the new language to the TLanguage type in the language definitions file:
ts
// packages/i18n/src/types/language.ts
export type TLanguage = "en" | "fr" | "your-lang";
  1. Add language configuration
    Include the new language in the list of supported languages:
ts
// packages/i18n/src/constants/language.ts
export const SUPPORTED_LANGUAGES: ILanguageOption[] = [
  { label: "English", value: "en" },
  { label: "Your Language", value: "your-lang" },
];
  1. Create translation files

    1. Create a new folder for your language under locales (e.g., locales/your-lang/).

    2. Add a translations.json file inside the folder.

    3. Copy the structure from an existing translation file and translate all keys.

  2. Update import logic
    Modify the language import logic to include your new language:

ts
private importLanguageFile(language: TLanguage): Promise<any> {
      switch (language) {
          case "your-lang":
          return import("../locales/your-lang/translations.json");
          // ...
      }
      }

Quality checklist

Before submitting your contribution, please ensure the following:

  • All translation keys exist in every language file.
  • Nested structures match across all language files.
  • ICU message formats are correctly implemented.
  • All languages load without errors in the application.
  • Dynamic values and pluralization work as expected.
  • There are no missing or untranslated keys.

Pro tips

  • When in doubt, refer to the English translations for context.
  • Verify pluralization works with different numbers.
  • Ensure dynamic values (e.g., {name}) are correctly interpolated.
  • Double-check that nested key access paths are accurate.

Happy translating! 🌍✨

Need help? Questions and suggestions

Questions, suggestions, and thoughts are most welcome. We can also be reached in our Forum.


SECURITY

Security policy

This document outlines the security protocols and vulnerability reporting guidelines for the Plane project. Ensuring the security of our systems is a top priority, and while we work diligently to maintain robust protection, vulnerabilities may still occur. We highly value the community’s role in identifying and reporting security concerns to uphold the integrity of our systems and safeguard our users.

Reporting a vulnerability

If you have identified a security vulnerability, submit your findings to [email protected].
Ensure your report includes all relevant information needed for us to reproduce and assess the issue. Include the IP address or URL of the affected system.

To ensure a responsible and effective disclosure process, please adhere to the following:

  • Maintain confidentiality and refrain from publicly disclosing the vulnerability until we have had the opportunity to investigate and address the issue.
  • Refrain from running automated vulnerability scans on our infrastructure or dashboard without prior consent. Contact us to set up a sandbox environment if necessary.
  • Do not exploit any discovered vulnerabilities for malicious purposes, such as accessing or altering user data.
  • Do not engage in physical security attacks, social engineering, distributed denial of service (DDoS) attacks, spam campaigns, or attacks on third-party applications as part of your vulnerability testing.

Out of scope

While we appreciate all efforts to assist in improving our security, please note that the following types of vulnerabilities are considered out of scope:

  • Vulnerabilities requiring man-in-the-middle (MITM) attacks or physical access to a user’s device.
  • Content spoofing or text injection issues without a clear attack vector or the ability to modify HTML/CSS.
  • Issues related to email spoofing.
  • Missing DNSSEC, CAA, or CSP headers.
  • Absence of secure or HTTP-only flags on non-sensitive cookies.

Our commitment

At Plane, we are committed to maintaining transparent and collaborative communication throughout the vulnerability resolution process. Here's what you can expect from us:

  • Response Time
    We will acknowledge receipt of your vulnerability report within three business days and provide an estimated timeline for resolution.
  • Legal Protection
    We will not initiate legal action against you for reporting vulnerabilities, provided you adhere to the reporting guidelines.
  • Confidentiality
    Your report will be treated with confidentiality. We will not disclose your personal information to third parties without your consent.
  • Recognition
    With your permission, we are happy to publicly acknowledge your contribution to improving our security once the issue is resolved.
  • Timely Resolution
    We are committed to working closely with you throughout the resolution process, providing timely updates as necessary. Our goal is to address all reported vulnerabilities swiftly, and we will actively engage with you to coordinate a responsible disclosure once the issue is fully resolved.

We appreciate your help in ensuring the security of our platform. Your contributions are crucial to protecting our users and maintaining a secure environment. Thank you for working with us to keep Plane safe.