traggo/server: self-hosted time tracking built on tags instead of tasks
self-hosted tag-based time tracking
At a glance
- What is it?
- Traggo stores time spans with tags and nothing else, so its reports depend entirely on how disciplined your tagging is. A review of its architecture, its Docker setup, and the cases where a task-based tracker fits better.
- Who is it for?
- Adopt traggo/server if you already think in tags and you are willing to run the container, back up the SQLite file, and treat tag names as a schema you must not break. Skip it if you need a task hierarchy, invoicing, or a mobile app, because the README does not document any of those.
- Can I use it commercially?
- Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
- Is it still maintained?
- Yes. The repository last received commits 4 days ago.
- What is it written in?
- Mainly Go, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What traggo/server replaces, and for whom
Most time trackers ask you to create a task first and attach time to it afterwards. Traggo removes that object. The README states plainly: "In Traggo there are no tasks, only tagged time spans." A time span is a start and an end; everything else is a tag. If you work across projects, the README suggests a project tag. If you want to know how your hours split between email, programming and meetings, it suggests a type tag. The set of tags is yours to define, which is the whole point and also the whole risk.
That design fits freelancers and small teams who already keep a mental taxonomy of their work and want reports that follow it. It fits people who want the data on their own hardware, since the README is explicit that you host it yourself and that no third party can read the result. It fits less well anyone who needs a client to sign off on a timesheet, because a tag has no invoice line, no hourly rate and no approval state in the model described.
The Go server, the GraphQL API and the SQLite file underneath
The repository is a Go module, github.com/traggo/server, built on go 1.24.0. The top-level directories map closely to the features: timespan, tag, dashboard, statistics, user, auth, database and graphql. The HTTP layer uses github.com/gorilla/mux, and the API is GraphQL, generated with gqlgen v0.17.85 from schema.graphql and gqlgen.yml. Persistence goes through github.com/jinzhu/gorm, and the .env.sample restricts the dialect to sqlite3, with the connection string pointing at a file such as data/traggo.db. The UI is a separate yarn project under ui/.
That layout tells you where the extension points are. Anything you want to compute that the statistics package does not already compute has to go through the GraphQL schema, which means regenerating code with gqlgen rather than dropping in a plugin. Anything you want to store is a row in a single SQLite file, which makes backup trivial and concurrent write scaling impossible. The README does not document a supported path to Postgres or MySQL; the indirect dependencies in go.mod list drivers for both, but the sample configuration names sqlite3 as the only dialect.
Running traggo/server with docker-compose
The repository ships a docker-compose.yml that builds from docker/Dockerfile.dev, so this is the development path rather than a published image. It sets the default credentials through environment variables and publishes port 3030. The volume line matters: it maps .traggodata to /opt/traggo/data, which is where the SQLite database lives.
services:
traggo:
environment:
TRAGGO_DEFAULT_USER_NAME: "admin"
TRAGGO_DEFAULT_USER_PASS: "admin"
TRAGGO_LOG_LEVEL: debug
ports:
- 3030:3030
volumes:
- .traggodata:/opt/traggo/dataAfter the container is up, open http://localhost:3030 and log in with admin and admin. Change both values before the instance is reachable from anywhere but your own machine, because the compose file as written ships a known password.
The .env.sample documents the same settings for a non-Docker run, plus two the compose file omits. TRAGGO_PORT defaults to 3030, and TRAGGO_PASS_STRENGTH controls bcrypt cost, described in the sample as "higher = more secure but also slower". TRAGGO_LOG_LEVEL accepts debug, info, warn, error, fatal or panic.
TRAGGO_PORT=3030
TRAGGO_DEFAULT_USER_NAME=admin
TRAGGO_DEFAULT_USER_PASS=admin
TRAGGO_PASS_STRENGTH=10
TRAGGO_LOG_LEVEL=info
TRAGGO_DATABASE_DIALECT=sqlite3
TRAGGO_DATABASE_CONNECTION=data/traggo.dbIf you prefer to build the binary yourself, the Makefile drives it. install-go runs go mod download, build-js runs yarn build inside ui/, and build-bin-local then compiles with CGO enabled and the sqlite tags.
make install-go
make build-bin-localThe first real use is to log a span. Create a project tag, then a type tag, then record a time span against both. The README does not give the GraphQL mutation for this, so the browser UI is the documented route; the dashboard, list and calendar views are where the tags you created show up as diagrams and rows.
Where the tag-only model breaks down
Tags are strings, and nothing in the described model constrains them. Type project:acme once and project:Acme later and your statistics treat them as two projects. There is no schema for tag values, no foreign key from a span to a canonical project record, and therefore no rename that rewrites history unless the server exposes one. The README does not document a tag migration command, so the safe assumption is that consistency is your job from day one.
The second limit is scale. SQLite behind GORM is fine for one person or a handful, and the README's feature list is aimed there: simple user management, a list view, a calendar view. It is not a description of a multi-tenant service. If your team grows past a few concurrent writers, the single-file database is the constraint you will hit first, and the sample configuration offers no other dialect to move to.
The third is the API surface. Because the client is generated from schema.graphql with gqlgen, an upgrade between minor versions can change the shape of queries your own scripts depend on. The repository has released v0.8.1, v0.8.2 and v0.8.3 within roughly two months of each other, following SemVer as the README states. SemVer permits breaking changes in a major version, not in these, but it does not guarantee that a field you rely on stays where it is. Pin the image tag and read the release notes before moving.
How traggo/server differs from Kimai and other task-first trackers
Kimai is the obvious comparison point for anyone shopping for a self-hosted time tracker: it is also open source and also self-hosted, but it is built around customers, projects, activities and invoices, with a relational schema that expects MySQL or MariaDB. The difference is not cosmetic. In Kimai the project is an entity with an id, so renaming it updates every entry that points at it. In Traggo the project is a tag value on each span, so the same rename is a bulk edit across rows. Kimai gives you billable rates and export formats aimed at invoicing; Traggo gives you tags and dashboards.
That trade cuts both ways. A tag model lets you attach a dimension you did not anticipate last month without a schema migration, which is exactly what the README means when it says Traggo tries to be as customizable as possible. A relational model gives you referential integrity, which a tag model cannot. If your reporting question is "how many hours did I spend on meetings this quarter", tags win. If it is "what do I invoice client X for March", the task-and-rate model wins, and no amount of dashboard configuration closes that gap.
Licence, upgrades and what maintenance costs you
Traggo is GPL-3.0. If you run it for yourself, the licence asks little of you. If you modify the server and distribute it, or offer it to others over a network, the copyleft terms attach to your modified source. That is a real consideration for anyone planning to embed the GraphQL API in a hosted product, and it is the kind of question to put to a lawyer rather than to a README.
The last push to the repository was on 2026-07-31, and the most recent release is v0.8.3 from 2026-03-01. The project is not archived. Upgrades are the usual container problem: the database is one SQLite file, so back it up before pulling a new image, and note that the compose file in the repository builds from docker/Dockerfile.dev rather than a pinned published tag. If you deploy from that file unchanged, you are tracking the master branch. Pin a release tag instead if you want reproducibility.
Running costs are small and mostly operational. One container, one file, no external database. The recurring cost is discipline: every new contributor to your tag vocabulary is a chance to fork your own reports.
Questions worth answering before you commit
The features the README lists are time tracking, customizable dashboards with diagrams, a list and calendar view, multiple UI themes and simple user management. Nothing there mentions mobile clients, offline capture, invoicing, or an export format. If any of those are on your requirements list, treat them as absent until you find them documented, because the README is the only description of scope the project offers.
The second thing to settle is where your data actually lives. The compose file writes to .traggodata on the host, and the .env.sample writes to data/traggo.db. Those are two different paths for the same file depending on how you launch it, and getting it wrong means your spans disappear on the next container rebuild. Decide which one you are using, put it on a backed-up volume, and only then start recording time you care about.
Editorial conclusion
Adopt traggo/server if you already think in tags and you are willing to run the container, back up the SQLite file, and treat tag names as a schema you must not break. Skip it if you need a task hierarchy, invoicing, or a mobile app, because the README does not document any of those. Before committing, check the release notes for the API changes between v0.8.1 and v0.8.3 and confirm that the login UI in your browser matches the TRAGGO_DEFAULT_USER_NAME and TRAGGO_DEFAULT_USER_PASS values in your compose file.
Frequently asked questions
Does traggo/server have tasks, or only tags?
Only tags. The README states that in Traggo there are no tasks, only tagged time spans, and it suggests tags such as project or type to organize them.
Which database does traggo/server use?
The .env.sample sets TRAGGO_DATABASE_DIALECT to sqlite3 and gives TRAGGO_DATABASE_CONNECTION an example value of data/traggo.db. The sample lists sqlite3 as the only permitted dialect.
What port does traggo/server listen on?
Port 3030. The .env.sample documents TRAGGO_PORT=3030, and the docker-compose.yml maps 3030:3030 on the host.
How do I change the default traggo/server admin password?
Set TRAGGO_DEFAULT_USER_NAME and TRAGGO_DEFAULT_USER_PASS before the instance is reachable. The docker-compose.yml ships both as admin, and TRAGGO_PASS_STRENGTH controls the bcrypt cost used for the stored password.
Is traggo/server free to self-host?
The repository is licensed GPL-3.0, and the README says you need to host it yourself so that you keep full control over your data.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/traggo-server)