Plane, Self-Hosted: Owning My Ticketing
Published by Dan Richardson at https://freepdx.com/deep-dives/plane-fork
- Status
- Production
The Problem
I needed project management tool for my development work but wanted something extensible and have the ability to host it myself. Plane’s open-source edition had about 80% of what I wanted - good enough to use, not good enough to leave alone. The community edition was missing some features I needed, and the Docker deployment was sluggish and painful to operate. So, what would it take to improve it?
What I Built
A self-hosted fork of Plane’s Community Edition, pinned at v1.2.3, that I own and run in production with all my real data. It runs faster, costs nothing per month, and works great with Throughline Build. Along the way, I added features for bulk management and AI-assisted ticket creation.
The Community Edition is great, but was not very DIY friendly. The stock path runs everything in Docker under compose. I ran a four-way runtime bake-off - Podman Compose (the painful baseline), a slim native design, a Podman Quadlet hybrid, and full native - and full native won outright. The production box runs PostgreSQL 15, Redis, RabbitMQ, and nginx directly on an LXC container, with the Django backend and Next.js frontend built from source and systemd managing the services. No container runtime in the stack at all. Restart times dropped under three seconds, and moving off Docker raised API throughput by 50% - which matters, because my AI agents hit the REST API constantly and were getting rate-limited under the old setup.
Tech Stack
- Python
- TypeScript
- Bash
- Django
- Next.js
- React
- PostgreSQL 15
- Redis
- RabbitMQ
- Cloudflare R2
- LXC / Proxmox
- systemd
- nginx
- Cloudflare Tunnel
- pytest
- custom stdlib load-test harness
How It Works
Beyond the runtime swap, I added what I actually needed. Bulk delete - the original reason for forking - went back in. An AI-enhanced ticketing path lets me paste raw text (notes, ideas, bug descriptions) and get back a well-formed, repo-aware ticket, which keeps traceability and documentation consistent without hand-formatting every issue.
The commercial and community editions diverge at the database level. I mapped the gap - one nullable column and three dropped commercial-only columns - into a repeatable migration runbook with a SQL bridge script, proved it on a copy of live data, then test-loaded the real database onto the new box before cutting over. I also modularized the OpenAPI utilities (cutting decorator code roughly 80%) and rebuilt the data export system around a typed, schema-based architecture with CSV, JSON, and XLSX output. A stdlib-only load-testing harness (hammer.py) exercised fetch-all, list, single-read, and write workloads across concurrency ladders to validate the box before I trusted production data to it.
What Made It Hard
Migrating real data without losing or corrupting a row. Adding an API to someone else’s schema without a database migration. And proving the native box had the headroom to run my daily workflow, which is where the load-test harness earned its keep.
The Throughline
This is the ticketing backend the rest of the work runs on. Every project I track and every ticket my agents create flows through it - it’s the system Latticeflow writes to as it builds the other projects on this site. Owning the tool means I shape it around my workflow instead of the reverse.
Status
Live and serving as my primary project management system since the cutover on June 15, 2026, at plane.throughlinetech.net via Cloudflare Tunnel. The old commercial instance is retired and powered off. The migration is proven and repeatable, the box survives reboots cleanly, and the load-test results give me confidence in its headroom. I pull upstream changes selectively and apply fork patches on top.