This Hits A Little Close To Home For Some Of Us
This Hits A Little Close To Home For Some Of Us
Why Documentation And Ticket Tracking Still Matter In A Self‑Hosted World
If you’ve ever stared at a cluttered terminal, opened a stack of half‑finished notes, or felt that uneasy pang when a ticket sits untouched for days, you’re not alone. The Reddit thread that sparked this conversation laid bare a universal truth: even in the most automated, self‑hosted environments, the human need to prove “I did something” persists.
For homelab enthusiasts, DevOps engineers, and seasoned sysadmins, the challenge isn’t just keeping services up and running. It’s also about making the invisible work visible, proving value, and protecting yourself from the “I‑don’t‑remember‑what‑I‑did‑last‑week” trap. This guide tackles that exact pain point. You’ll learn how to build a lightweight, yet powerful, ticket‑tracking and documentation workflow that integrates cleanly with the tools you already run in your self‑hosted stack.
By the end of this post you will:
- Understand why traditional ticketing systems still matter for infrastructure work.
- Choose the right open‑source tools that fit a homelab or small‑scale production environment.
- Follow a step‑by‑step installation and configuration process that avoids common pitfalls.
- Apply best‑practice hardening, performance, and scaling tips for long‑term reliability.
- Troubleshoot the most frequent issues without resorting to endless log‑scrolling.
All of this is presented for an audience that already knows the basics of Docker, systemd, and basic networking, but wants a more structured way to track, document, and prove their infrastructure efforts.
Understanding The Topic
What Are We Actually Talking About?
At its core, the discussion revolves around work‑tracking and documentation for infrastructure automation. This includes:
- Ticket creation and status management for tasks like “upgrade PostgreSQL”, “migrate backups”, or “refactor Docker Compose network”.
- Markdown‑based notes stored alongside code repositories, ensuring that every change is accompanied by a rationale.
- Automation hooks that automatically log actions (e.g., a GitLab CI job that updates a ticket when a PR merges).
The technology stack that supports this workflow can be as simple as a Kanban board in GitHub Projects or as robust as a full‑featured open‑source issue tracker like YouTrack or Taiga. For most homelab owners, the sweet spot lies in lightweight, self‑hosted solutions that can be containerized and integrated with existing services.
A Brief History
The concept of “ticketing” originated in software development labs where engineers needed a way to track bugs, feature requests, and operational tasks. Early systems like Bugzilla (1996) and later JIRA (2004) introduced databases, workflows, and customizable fields.
In the DevOps era, the rise of Infrastructure as Code (IaC) and GitOps shifted the focus from isolated bug trackers to holistic work management that lives alongside code. Tools such as GitHub Issues, GitLab Issues, and Phabricator now provide APIs that let you tie a commit, a deployment, or a container restart to a specific ticket number.
For self‑hosted environments, the last decade has seen a surge of open‑source, self‑contained ticket systems that can run on a single VM or inside Docker containers. Projects like Wekan, Gitea’s Issue Tracker, and Redmine offer varying degrees of customization, but the most common denominator is the need for clear documentation that proves you’ve done the work.
Key Features And Capabilities
A solid ticket‑tracking system for infrastructure should provide:
- Issue Lifecycle Management – From “Opened” to “In Progress” to “Closed”, with customizable statuses.
- Linking To Code – Ability to reference commits, branches, or container images directly from an issue.
- Automation Hooks – Webhooks or APIs that trigger scripts (e.g., a post‑deployment script that marks a ticket as resolved).
- Rich Text Documentation – Markdown support for detailed, version‑controlled notes.
- Search And Tagging – Quickly locate tickets by label, project, or keyword.
These capabilities address the exact concerns raised in the Reddit thread: “How else am I supposed to document that I’ve done something?” and “Look if you documented a rare, unique, or unsolved issue you deserve the self medal.”
Pros And Cons
| Advantage | Explanation |
|---|---|
| Self‑hosted control | You own the data, can enforce security policies, and avoid vendor lock‑in. |
| Integration with CI/CD | Native webhooks let you automatically close tickets when a deployment succeeds. |
| Version‑controlled notes | Markdown files stored in Git ensure that every change is auditable. |
| Community support | Open‑source projects often have active forums, Docker images, and extensive docs. |
| Drawback | Mitigation |
|---|---|
| Initial setup overhead | Use pre‑built Docker images and follow a documented installation script. |
| Learning curve for workflow customization | Start with a minimal configuration, then iterate based on real‑world usage. |
| Resource consumption | Run the tracker in a lightweight container; monitor CPU and memory with docker stats. |
Real‑World Applications
- Homelab Backup Automation – A ticket tracks “Weekly encrypted backup of Nextcloud”. When the cron job finishes, a webhook updates the ticket status to “Completed”.
- Network Refactor – Updating VLAN configurations across multiple Proxmox nodes is logged as a ticket with step‑by‑step Markdown instructions stored in the repo.
- Service Migration – Migrating a legacy web service from Docker Compose to Kubernetes is broken into sub‑tasks, each with its own ticket and linked PR.
These scenarios illustrate why documenting the “what” and the “why” is as critical as the technical execution itself.
Prerequisites
Before you dive into installation, verify that your environment meets the following baseline requirements.
System Requirements
| Component | Minimum Version | Reason |
|---|---|---|
| CPU | 2 cores | Needed for container orchestration and ticket processing. |
| RAM | 2 GB | Sufficient for Docker Engine and the ticketing container. |
| Disk | 20 GB free | Holds images, logs, and persistent data. |
| OS | Ubuntu 22.04 LTS (or Debian 12) | Well‑supported packages and long‑term security updates. |
Required Software
| Software | Version | Installation Command |
|---|---|---|
| Docker Engine | 24.0+ | curl -fsSL https://get.docker.com | sh && sudo usermod -aG docker $USER |
| Docker Compose | 2.24+ | sudo apt-get install -y docker-compose-plugin |
| Git | 2.34+ | sudo apt-get install -y git |
| SQLite (for lightweight DB) | 3.39+ | sudo apt-get install -y sqlite3 |
Network And Security
- Port 8080 (or another free port) must be open for the web UI.
- Firewall – Allow inbound traffic on the chosen port and restrict access to trusted IPs only.
- TLS – Consider terminating HTTPS with a reverse proxy (Caddy or Nginx) and obtaining a cert via Let’s Encrypt.
User Permissions
- Create a dedicated system user for running the ticketing container, e.g.,
ticketbot. - Ensure the user belongs to the
dockergroup to execute Docker commands withoutsudo.
Pre‑Installation Checklist
- Verify Docker is running:
docker version. - Pull the ticketing image (see Installation section).
- Create a persistent volume directory:
mkdir -p /opt/ticketdata. - Back up any existing configuration files.
Installation & Setup
Below is a complete, reproducible installation workflow using Docker Compose. All commands are annotated with explanations to help you understand each step.
1. Pull The Official Ticketing Image
The example uses Wekan, a popular open‑source Kanban board that can be self‑hosted with minimal fuss.
1
docker pull $CONTAINER_IMAGE wekan/wekan:latest
Why $CONTAINER_IMAGE? Using a variable makes the command portable across scripts and avoids Jekyll‑specific placeholder conflicts.
2. Create a Docker Compose File
Save the following YAML as docker-compose.yml in a dedicated directory (/opt/ticketstack).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
version: "3.8"
services:
wekan:
image: $CONTAINER_IMAGE wekan/wekan:latest
container_name: $CONTAINER_NAMES wekan
restart: unless-stopped
environment:
- ROOT_URL=http://localhost:8080
- PORT=8080
- MONGO_URL=mongodb://mongo:27017/wekan
- BCryptCost=12
- SecretKey=CHANGE_THIS_TO_A_RANDOM_64CHAR_STRING
ports:
- "8080:8080"
volumes:
- $CONTAINER_VOLUME:/app/data
depends_on:
- mongo
mongo:
image: mongo:6.0
container_name: $CONTAINER_NAMES mongo
restart: unless-stopped
volumes:
- $CONTAINER_VOLUME/mongo:/data/db
Explanation of key fields:
$CONTAINER_IMAGE– Holds the Docker image name; replace with your registry if needed.$CONTAINER_NAMES– Unique identifier for the container; helps in logs and monitoring.$CONTAINER_VOLUME– Path to persistent storage; ensures data survives container restarts.ROOT_URL– The public URL the app will be reachable at.SecretKey– Must be a strong, random string; generate withopenssl rand -hex 32.
3. Initialize Persistent Storage
1
mkdir -p $CONTAINER_VOLUME
If you prefer a named volume, you can define it in the compose file and let Docker manage it.
4. Launch The Stack
1
docker-compose up -d
Verify that both containers are healthy:
1
docker ps
You should see wekan and mongo listed with a STATUS of Up.
5. Secure The Installation
a. Generate a Strong Secret Key
1
2
SECRET=$(openssl rand -hex 32)
sed -i "s/CHANGE_THIS_TO_A_RANDOM_64CHAR_STRING/$SECRET/g" docker-compose.yml
b. Set Up a Reverse Proxy (Optional but Recommended)
Create a simple Caddyfile:
:80 {
reverse_proxy localhost:8080
encode gzip
}
Run Caddy with Docker:
1
2
3
4
5
6
7
docker run -d \
--name caddy \
-p 80:80 \
-v $(pwd)/Caddyfile:/etc/caddy/Caddyfile \
-v caddy_data:/data \
-v caddy_config:/config \
caddy:latest
Now access the ticketing UI at http://yourdomain.com.
6. Create An Admin Account
- Open the UI in a browser.
- Register a new account using an email address you control.
- During registration, set the Role to
Admin. - Save the credentials in a password manager; never store them in plain text.
7. Verify Integration With Git
If you host your infrastructure playbooks in Git, you can link tickets to commits:
1
2
git commit -m "Close #123: Update PostgreSQL to 15.4"
git push origin main
When the commit is pushed, a webhook (configured later) can automatically transition ticket #123 to Closed.
Configuration & Optimization
1. Core Configuration Options
| Setting | Location | Default | Recommended Value | Impact |
|---|---|---|---|---|
BCryptCost | environment | 12 | 12 (or 13 for stronger hashing) | Determines password hash difficulty; higher values increase CPU usage. |
SecretKey | environment | Random | Unique per instance | Prevents session hijacking. |
PORT | environment | 8080 | 80 (behind proxy) | Aligns with standard HTTP ports; keep internal port unchanged. |
ROOT_URL | environment | http://localhost:8080 | https://tickets.yourdomain.com | Enables correct link generation in emails. |
Edit the docker-compose.yml file and modify the relevant environment variables. Then run docker-compose up -d again to apply changes.
2. Security Hardening
- Network Isolation – Place the container in a dedicated Docker network and block external access to the MongoDB port.
- Read‑Only Filesystem – Run the container with
--read-onlyand mount only the necessary volumes. - User Namespaces – Enable Docker’s user namespace remapping to avoid running as root inside the container.
Example Docker run command with hardening flags:
1
2
3
4
5
6
7
docker run -d \
--name $CONTAINER_NAMES wekan \
--restart unless-stopped \
--read-only \
--network $CONTAINER_NETWORK \
-e ROOT_URL=$ROOT_URL \
-