Plane Architecture Overview
Plane's three-tier architecture — database, API, frontend, state management, and real-time services
Plane Architecture Overview
For Product Managers
This page explains how Plane is structured as a software system. You don't need to understand every technical detail — focus on the diagrams and the "Why It Matters" callouts to build a mental model of how the product works under the hood.
Plane uses a three-tier architecture with React applications for the UI, a Django and DRF backend for business logic, PostgreSQL and supporting services for persistence, and Hocuspocus plus Celery for collaboration and background work. This page breaks down how those layers fit together and why that structure supports self-hosting, real-time editing, and product complexity.
How Plane's Architecture Works
Plane follows a three-tier architecture with a real-time collaboration layer, served through a Caddy reverse proxy:
┌─────────────────────────────────────────────────────────────────┐
│ USERS (Browser) │
└──────────────────────────────┬──────────────────────────────────┘
│
┌──────────▼──────────┐
│ Caddy Proxy (2.10) │ ← automatic HTTPS
│ Port 80 / 443 │ routes by path
└──┬──────┬───────┬───┘
│ │ │
┌───────────────▼─┐ ┌──▼─────┐ ┌▼───────────────┐
│ Web / Admin / │ │ API │ │ Live Server │
│ Space (React) │ │(Django │ │ (Hocuspocus) │
│ Nginx │ │ DRF) │ │ WebSocket CRDT │
│ :3000/3001/3002 │ │ :8000 │ │ :3100 │
└──────────────────┘ └──┬─────┘ └─────────────────┘
│
┌────────────────┼────────────────┐
│ │ │
┌──────▼──────┐ ┌──────▼──────┐ ┌───────▼───────┐
│ PostgreSQL │ │ Valkey │ │ MinIO / S3 │
│ 15.7 │ │ 7.2.11 │ │ (File Storage)│
│ (Primary DB) │ │ (Cache) │ │ │
└──────────────┘ └─────────────┘ └───────────────┘
│
┌──────▼──────┐
│ RabbitMQ │
│ 3.13.6 │
│ (MQ Broker) │
└──────┬──────┘
│
┌──────▼──────┐
│ Celery │
│ Workers │
│ + Beat │
└─────────────┘Why It Matters
Every user request flows through Caddy → the appropriate service. REST API calls hit Django; real-time editing uses WebSockets to the Hocuspocus live server. Background work (email notifications, cleanup, imports) runs through Celery workers using RabbitMQ as the message broker. Valkey (Redis-compatible) handles caching and sessions. This separation means the API stays fast even when heavy async work is running.
The Three Tiers
1. Frontend Layer (React 18 + MobX)
The frontend is built with React 18, React Router 7 (with SSR support), and MobX for state management. Three separate apps share packages:
| App | Port | Purpose |
|---|---|---|
| web | 3000 | Main user-facing application (issues, boards, pages) |
| admin | 3001 | Instance administration (/god-mode/) |
| space | 3002 | Public-facing project views (shareable links) |
MobX Store Hierarchy (180+ files):
RootStore
├── router.store → routing state
├── theme.store → UI theme
├── instance.store → instance config
├── user/
│ ├── profile.store → current user
│ ├── account.store → auth account
│ └── settings.store → user preferences
├── workspace/
│ ├── index.ts → workspace data
│ ├── home.ts → home dashboard
│ ├── webhook.store → webhook management
│ └── api-token.store → API tokens
├── project/
│ ├── project.store → project CRUD
│ └── project_filter → view filters
├── issue/ → issue stores per context
│ ├── project/ → project issue list
│ ├── cycle/ → cycle issues
│ ├── module/ → module issues
│ └── workspace-draft/ → draft issues
├── pages/ → wiki pages
├── notifications/ → notification center
├── timeline/ → activity timeline
└── ...Why MobX + SWR?
MobX provides fine-grained reactivity: when a single issue's title changes, only the components rendering that title re-render. SWR complements MobX by handling data fetching and cache invalidation. MobX stores hold the canonical state; SWR fetches and revalidates from the API.
2. API Layer (Django 4.2 + DRF)
The backend is Django 4.2.29 LTS with Django REST Framework 3.15.2, living at apps/api/.
URL Routing Structure:
/api/ → plane.app.urls (main app — workspaces, projects, issues)
/api/public/ → plane.space.urls (public/anonymous endpoints)
/api/instances/ → plane.license.urls (instance management)
/api/v1/ → plane.api.urls (API key–based access)
/auth/ → plane.authentication (login, OAuth, magic links)App-level URL modules (20+):
plane/app/urls/
├── analytic.py, api.py, asset.py, cycle.py, estimate.py
├── external.py, intake.py, issue.py, module.py, notification.py
├── page.py, project.py, search.py, state.py, user.py
├── views.py, webhook.py, workspace.py, timezone.py, exporter.pyKey API Patterns:
| Pattern | Description |
|---|---|
| Hierarchical URLs | Workspaces → Projects → Issues/Cycles/Modules |
| Standard REST | GET, POST, PATCH, DELETE |
| Throttling | 30/min anon, 60/min API key, custom per-endpoint |
| Serializer validation | DRF serializers validate all input/output |
| API documentation | drf-spectacular (OpenAPI/Swagger) |
3. Database Layer (PostgreSQL 15.7)
PostgreSQL with 30+ model files and 126 migrations.
Core Entity-Relationship Map:
┌──────────────┐
│ Workspace │ ← multi-tenancy root
└──────┬───────┘
│ has many
┌───────────┼───────────┐
│ │ │
┌──────▼──────┐ │ ┌──────▼──────┐
│ Project │ │ │ Member │
└──────┬───────┘ │ └─────────────┘
│ │
┌──────────┼──────┬───┼──────┐
│ │ │ │ │
┌─────▼────┐ ┌──▼───┐ ┌▼───▼─┐ ┌──▼─────┐
│ Issue │ │Cycle │ │Module│ │ State │
│ (central)│ │ │ │ │ │ │
└────┬─────┘ └──┬───┘ └──┬───┘ └────────┘
│ │ │
│ CycleIssue ModuleIssue
│ (M2M) (M2M)
│
┌────┼────┬──────────┬─────────┐
│ │ │ │ │
Comment Activity Reaction AttachmentBase Model Pattern — all workspace models inherit:
| Field | Purpose |
|---|---|
id | UUID primary key |
created_at | Auto-set on creation |
updated_at | Auto-set on every save |
created_by | Foreign key to User |
updated_by | Foreign key to User |
deleted_at | Soft-delete timestamp (null = active) |
workspace | Foreign key enforcing multi-tenancy |
Key Design Patterns
Multi-Tenancy
Every query is scoped to a workspace — enforced at the view level so no data leaks between organizations.
Soft Deletion
Records are never physically deleted. deleted_at is set, and a daily Celery beat task permanently removes records older than HARD_DELETE_AFTER_DAYS.
Activity Audit Trail
Every change to an Issue generates an IssueActivity record tracking what changed, who changed it, and when.
Community / Enterprise Gating
The web app has ce/ (community edition) and ee/ (enterprise edition) directories, enabling feature gating between the free and paid versions.
Real-Time Collaboration
The Live server (apps/live/) runs Hocuspocus on port 3100:
| Component | Technology |
|---|---|
| Server | Hocuspocus 2.15.2 (Node.js) |
| CRDT | Yjs 13.6.20 |
| Editor binding | y-prosemirror (Yjs ↔ ProseMirror) |
| Persistence | @hocuspocus/extension-database + Redis |
| Transport | WebSocket via express-ws |
When users edit Pages collaboratively, the Hocuspocus server merges their changes using CRDT math, persists to the database, and broadcasts updates to all connected clients.
Performance Architecture
Database
- Indexes on frequently queried fields (workspace_id, project_id, state_id)
- Read replica support for horizontal scaling
API
- Valkey caching for frequently accessed data
- Throttling at multiple levels (anon, API key, asset, email verification)
- GZip middleware for response compression
Frontend
- MobX computed values — derived state only recalculates when dependencies change
- Code splitting — lazy-loaded routes via React Router
- SWR caching — stale-while-revalidate pattern for API data