Self-Hosting
One image. One command.
Your whole workspace.
udctl ships as a single all-in-one container — frontend and backend baked together. Run it on your own infrastructure with one command. No vendor lock-in, and nothing ever leaves your network.
# the all-in-one image: frontend + backend in one container # only you can log in; add -e REGISTRATION_ENABLED=true to let others sign up docker run -d -p 3000:8080 \ -e HOST_DOMAIN=http://localhost:3000 \ -e JWT_SECRET=change-me-to-a-random-string \ -e ADMIN_EMAIL=admin@example.com \ -e ADMIN_PASSWORD=changeme \ -e LICENSE_TOKEN=eyJjcmVhdGVkX2F0IjoxNzkxMTY5MDU3LCJ2YWxpZF91bnRpbCI6MTc5ODk0NTA1NywidGllciI6InBybyIsInVzZXJfbmFtZSI6IlNlbGYtSG9zdCBUcmlhbCIsInVzZXJfc2VjcmV0X2hhc2giOiJlZDJiYTE3OGM2NzBlYzdmZjA0NzdlZTc1MTAyNjVkM2M4NWUzN2QyNWI2NDcxNDc3OTBmZDJmZDJlMTI1YmQ4Iiwibm9uY2UiOiI0NmU0NTE5Mi04NWI0LTQzZDUtOTczYS1kMzg2MDc2NmZhNWMiLCJtYXhfdXNlcnMiOjV9.uP9ZL2efUQbLQAVa3RMgNVJi7br0o3i44yE64lhev21Gpw8_yp8Qs1jgKASLpEHsQgusS8oqq5QtVy7y5L0fBQ \ -e LICENSE_HOST_SECRET=undercontrol-selfhost-trial \ -v undercontrol-data:/app/data \ lintao0o0/undercontrol:latest
Why self-host
Your infrastructure, your rules.
Full data ownership
Tasks, notes, files, and finances live on hardware you control. Nothing is sent to a third party — not even for AI, which runs against your own provider.
No vendor lock-in
Everything-as-code. Your data is declarable in Markdown and YAML, versionable with Git, and exportable any time. No proprietary formats to escape.
Offline & air-gapped
Run inside a private network or a fully air-gapped environment. udctl keeps working without any outbound internet access.
Pick your setup
Choose your database, storage, backups, and scaling. A Raspberry Pi, a home server, a VPS, or a Kubernetes cluster — the choice is entirely yours.
Progressive deployment
Start small. Grow in place.
Start right on your own laptop with the desktop app — no Docker, no server. Graduate to a self-hosted container when you want multi-device access, then scale to PostgreSQL, cloud storage, or a full cluster. Same data model the whole way — you flip environment variables, never re-architect.
Local desktop app
Download & run. Built-in backend, fully offline. No Docker, no server.
All-in-one · SQLite
One container on your own server. Reach it from any device.
Dedicated PostgreSQL
Concurrent access and production-grade reliability.
Cloud file storage
Offload attachments to S3, R2, or MinIO.
Kubernetes cluster
High availability, autoscaling, rolling updates.
One root directory — fully portable
Everything you own lives under a single root data directory. Moving your entire workspace — from your desktop app to a self-hosted server, or from one server to another — is a single copy of ./data. No proprietary export, no re-import. Back it up, move it, restore it, done.
Free trial license
Start on Pro — on us, until 2027-01-03.
We're in active development, so this Pro license is free for everyone to use. It's already wired into every deployment below — just paste and go.
udctl Pro — Self-Host Trial
Unlocks PostgreSQL, S3/R2 storage, multi-user with roles, and the admin dashboard.
# udctl - free Pro trial license (valid until 2027-01-03) LICENSE_TOKEN=eyJjcmVhdGVkX2F0IjoxNzkxMTY5MDU3LCJ2YWxpZF91bnRpbCI6MTc5ODk0NTA1NywidGllciI6InBybyIsInVzZXJfbmFtZSI6IlNlbGYtSG9zdCBUcmlhbCIsInVzZXJfc2VjcmV0X2hhc2giOiJlZDJiYTE3OGM2NzBlYzdmZjA0NzdlZTc1MTAyNjVkM2M4NWUzN2QyNWI2NDcxNDc3OTBmZDJmZDJlMTI1YmQ4Iiwibm9uY2UiOiI0NmU0NTE5Mi04NWI0LTQzZDUtOTczYS1kMzg2MDc2NmZhNWMiLCJtYXhfdXNlcnMiOjV9.uP9ZL2efUQbLQAVa3RMgNVJi7br0o3i44yE64lhev21Gpw8_yp8Qs1jgKASLpEHsQgusS8oqq5QtVy7y5L0fBQ LICENSE_HOST_SECRET=undercontrol-selfhost-trial
Deployment methods
Copy a compose file, and you're running.
Every method below has the trial license baked in. The all-in-one image is the fastest path; the others are there when you want a dedicated database or a cluster.
services: undercontrol: image: lintao0o0/undercontrol:latest container_name: undercontrol ports: - "3000:8080" environment: - GIN_MODE=release - HOST_DOMAIN=http://localhost:3000 - JWT_SECRET=change-me-to-a-random-string - ADMIN_EMAIL=admin@example.com - ADMIN_PASSWORD=changeme # let other people sign up too (default: only you can log in) # - REGISTRATION_ENABLED=true # free Pro trial license (valid until 2027-01-03) - LICENSE_TOKEN=eyJjcmVhdGVkX2F0IjoxNzkxMTY5MDU3LCJ2YWxpZF91bnRpbCI6MTc5ODk0NTA1NywidGllciI6InBybyIsInVzZXJfbmFtZSI6IlNlbGYtSG9zdCBUcmlhbCIsInVzZXJfc2VjcmV0X2hhc2giOiJlZDJiYTE3OGM2NzBlYzdmZjA0NzdlZTc1MTAyNjVkM2M4NWUzN2QyNWI2NDcxNDc3OTBmZDJmZDJlMTI1YmQ4Iiwibm9uY2UiOiI0NmU0NTE5Mi04NWI0LTQzZDUtOTczYS1kMzg2MDc2NmZhNWMiLCJtYXhfdXNlcnMiOjV9.uP9ZL2efUQbLQAVa3RMgNVJi7br0o3i44yE64lhev21Gpw8_yp8Qs1jgKASLpEHsQgusS8oqq5QtVy7y5L0fBQ - LICENSE_HOST_SECRET=undercontrol-selfhost-trial volumes: - ./data:/app/data restart: unless-stopped
Bring it up
Save the file, then run docker compose up -d. First boot pulls the image and runs migrations automatically.
Architecture
One image by default — split it out when you need to.
The all-in-one image serves the web app and the Go API from one container. Bring your own PostgreSQL or SQLite, and plug in an OpenAI-compatible AI provider, S3-compatible storage, or OpenTelemetry — all optional, all under your control.
Ready to own your workspace?
Paste the compose file above, or follow the full guide for PostgreSQL, S3, and Kubernetes setups.