4.1 KiB
Deployment
Local Development
-
Copy the example environment file and edit it:
cp .env.example .env -
Start PostgreSQL (e.g. via Docker):
docker run -d --name postgres \ -e POSTGRES_USER=user -e POSTGRES_PASSWORD=password -e POSTGRES_DB=forgejo_tickets \ -p 5432:5432 postgres:16-alpine -
Run the application:
make runThis runs
go run ./cmd/server, which auto-migrates the database on startup. -
For CSS development, run the Tailwind watcher in a separate terminal:
make tailwind-watch
The public UI is at http://localhost:8080 and the admin panel at http://localhost:8081.
Makefile Targets
| Target | Description |
|---|---|
build |
Compiles Tailwind CSS then builds the Go binary (forgejo-tickets) |
run |
Runs the server directly with go run (no prior build needed) |
test |
Runs all Go tests (go test ./...) |
tailwind |
Compiles Tailwind CSS from web/static/css/input.css to web/static/css/output.css (minified) |
tailwind-watch |
Runs Tailwind in watch mode for development |
docker |
Builds the Docker image tagged with the git SHA and latest |
docker-push |
Builds and pushes the Docker image to the registry |
clean |
Removes the compiled binary and generated CSS |
Docker
The Dockerfile uses a 3-stage build:
- Node/Tailwind stage (
node:22-alpine) — Compiles Tailwind CSS. - Go build stage (
golang:1.23-alpine) — Downloads dependencies and compiles the Go binary withCGO_ENABLED=0. - Runtime stage (
alpine:3.20) — Minimal image with just the binary, templates, and static assets. Exposes ports 8080 and 8081.
Build and run:
make docker
docker run --env-file .env -p 8080:8080 -p 8081:8081 registry.ts.mattnite.net/forgejo-tickets:latest
Nomad
The production deployment uses Nomad with the job spec at infra/tickets/tickets.hcl.
Job Structure
- Job:
tickets(service type, datacenterdc1) - Group:
tickets(count 1)- Two ports on the Tailscale host network:
http(public) andadmin
- Two ports on the Tailscale host network:
- Task:
server(Docker driver)
Environment Injection
Secrets are stored as Nomad variables at the path nomad/jobs/tickets and injected via a template block:
DATABASE_URL,SESSION_SECRETFORGEJO_URL,FORGEJO_API_TOKENPOSTMARK_SERVER_TOKENGOOGLE_CLIENT_ID,GOOGLE_CLIENT_SECRETMICROSOFT_CLIENT_ID,MICROSOFT_CLIENT_SECRETAPPLE_CLIENT_ID,APPLE_TEAM_ID,APPLE_KEY_ID
Non-secret configuration is set directly in the env block:
PUBLIC_ADDRandADMIN_ADDRuse Nomad's dynamic port variablesBASE_URLis the public-facing URLPOSTMARK_FROM_EMAILis the sender addressTAILSCALE_ALLOWED_USERScontrols admin access
Traefik Routing
Two Nomad services are registered with Traefik tags:
| Service | Host | Purpose |
|---|---|---|
tickets |
tickets.ts.mattnite.net |
Public UI (internal Traefik, TLS via Let's Encrypt) |
tickets-admin |
tickets-admin.ts.mattnite.net |
Admin panel (internal Traefik, TLS via Let's Encrypt) |
Both use the websecure entrypoint with automatic TLS certificate resolution.
Health Checks
The public service registers an HTTP health check at GET /health with a 10-second interval and 30-second timeout.
Logging
Docker logging is configured to ship to Loki at loki.ts.mattnite.net with job name and allocation ID as labels.
Resources
- CPU: 200 MHz
- Memory: 256 MB
Database
The application uses PostgreSQL with GORM as the ORM. On startup, database.RunMigrations() calls models.AutoMigrate() which:
- Creates PostgreSQL enum types (
ticket_status,token_type) if they don't exist - Auto-migrates all model tables (User, OAuthAccount, Session, Repo, Ticket, TicketComment, EmailToken)
- Creates composite and partial unique indexes
No separate migration tool is needed — the schema is managed entirely through GORM auto-migration.
See Configuration for all environment variables and Forgejo Integration for webhook URL setup.