Self-Host TimeTagger with Docker Compose
What Is TimeTagger?
TimeTagger is a lightweight, open-source time tracking tool that uses tags instead of rigid project structures. Its interactive timeline view lets you drag and drop time entries, set daily or weekly targets, and export reports as PDF or CSV. It runs on Python with an embedded SQLite database — no external database needed. It replaces Toggl ($10-20/user/month), Clockify, and Harvest.
Updated February 2026: Verified with latest Docker images and configurations.
Official site: timetagger.app | GitHub
Prerequisites
- A Linux server (Ubuntu 22.04+ recommended)
- Docker and Docker Compose installed (guide)
- 256 MB of free RAM
- A domain name (recommended for HTTPS — passwords are sent in plaintext over HTTP)
Docker Compose Configuration
Create a docker-compose.yml file:
services:
timetagger:
image: ghcr.io/almarklein/timetagger:v26.1.3
restart: unless-stopped
ports:
- "8080:80"
volumes:
- timetagger_data:/root/_timetagger
environment:
# Bind to all interfaces on port 80 inside the container
TIMETAGGER_BIND: "0.0.0.0:80"
# Data directory inside the container
TIMETAGGER_DATADIR: "/root/_timetagger"
# Log level: debug, info, warning, error
TIMETAGGER_LOG_LEVEL: "info"
# User credentials — CHANGE THIS
# Format: username:bcrypt_hash
# Generate hashes at https://timetagger.app/cred
TIMETAGGER_CREDENTIALS: "${TT_CREDENTIALS}"
volumes:
timetagger_data:
Create a .env file alongside your docker-compose.yml:
# Generate your credential hash at https://timetagger.app/cred
# Format: username:bcrypt_hash
# Example (user: admin, password: changeme):
TT_CREDENTIALS=admin:$$2a$$08$$0CD1NFiIbancwWsu3se1v.RNR/b7YeZd71yg3cZ/3whGlyU6Iny5i
Important: In .env files, $ signs in bcrypt hashes must be escaped as $$.
Start the stack:
docker compose up -d
Initial Setup
- Open
http://your-server-ip:8080in your browser - Log in with the username and password you configured in the credentials
- You’ll see the interactive timeline — start tracking time by clicking the start button or dragging on the timeline
- Add tags to your time entries by typing
#tagnamein the description
Non-Root Variant (Recommended for Security)
For production deployments, use the non-root image variant:
services:
timetagger:
image: ghcr.io/almarklein/timetagger:v26.1.3-nonroot
restart: unless-stopped
ports:
- "8080:80"
volumes:
- timetagger_data:/opt/_timetagger
environment:
TIMETAGGER_BIND: "0.0.0.0:80"
TIMETAGGER_DATADIR: "/opt/_timetagger"
TIMETAGGER_LOG_LEVEL: "info"
TIMETAGGER_CREDENTIALS: "${TT_CREDENTIALS}"
volumes:
timetagger_data:
Note the volume path changes from /root/_timetagger to /opt/_timetagger for the non-root variant.
Configuration
Adding Multiple Users
Separate multiple user credentials with commas:
TT_CREDENTIALS=alice:$$2a$$08$$hash1here,bob:$$2a$$08$$hash2here
Each user gets their own isolated time tracking data.
Key Settings
| Environment Variable | Default | Purpose |
|---|---|---|
TIMETAGGER_BIND | 127.0.0.1:8080 | Server address and port |
TIMETAGGER_DATADIR | ~/_timetagger | SQLite database location |
TIMETAGGER_LOG_LEVEL | info | Logging verbosity |
TIMETAGGER_CREDENTIALS | (none) | User authentication |
TIMETAGGER_PATH_PREFIX | /timetagger/ | URL path prefix |
TIMETAGGER_APP_REDIRECT | false | Redirect root to app |
Proxy Authentication
If you use Authelia, Authentik, or another SSO proxy, TimeTagger supports proxy auth:
environment:
TIMETAGGER_PROXY_AUTH_ENABLED: "true"
TIMETAGGER_PROXY_AUTH_TRUSTED: "172.16.0.0/12"
TIMETAGGER_PROXY_AUTH_HEADER: "X-Remote-User"
This trusts the X-Remote-User header from your authentication proxy for automatic login.
Reverse Proxy
TimeTagger sends passwords in plaintext over HTTP — HTTPS via a reverse proxy is strongly recommended for production.
Example Nginx Proxy Manager configuration:
| Field | Value |
|---|---|
| Domain | time.yourdomain.com |
| Scheme | http |
| Forward Hostname | your-server-ip |
| Forward Port | 8080 |
| SSL | Request a new certificate |
For more details: Reverse Proxy Setup
Backup
TimeTagger stores everything in a single SQLite database file inside the data volume.
docker run --rm -v timetagger_data:/data -v $(pwd):/backup alpine \
cp /data/timetagger.db /backup/timetagger-backup-$(date +%Y%m%d).db
For a comprehensive approach: Backup Strategy
Troubleshooting
Login Not Working
Symptom: Credentials rejected at login page
Fix: Verify your bcrypt hash. Generate a fresh one at timetagger.app/cred. In Docker Compose YAML, escape $ as $$. In .env files, also use $$ escaping. Test by printing the variable:
docker compose exec timetagger printenv TIMETAGGER_CREDENTIALS
Timeline Not Loading
Symptom: Page loads but timeline is blank or shows errors Fix: Check browser console for JavaScript errors. TimeTagger requires a modern browser with WebSocket support. Try clearing browser cache or using incognito mode. Verify the container is healthy:
docker compose logs timetagger
Data Not Persisting After Restart
Symptom: Time entries disappear after docker compose down && docker compose up
Fix: Ensure you’re using a named volume (not a bind mount typo). Check that TIMETAGGER_DATADIR matches the volume mount path. For the default image, data is at /root/_timetagger; for non-root, it’s at /opt/_timetagger.
Resource Requirements
| Resource | Usage |
|---|---|
| RAM | 50-100 MB idle, 150 MB under load |
| CPU | Minimal — single-core adequate |
| Disk | ~50 MB for application, <10 MB per active user |
TimeTagger is one of the lightest time tracking tools available. It can run comfortably on a Raspberry Pi.
Verdict
TimeTagger is the best choice for individuals and small teams who want simple, flexible time tracking without the overhead of project management features. Its tag-based approach is more intuitive than Kimai’s project/activity hierarchy, and it uses a fraction of the resources. If you need invoicing, team management, or client billing, Kimai is the better fit. For pure time tracking with a beautiful timeline interface, TimeTagger is unmatched.
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments