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

  1. Open http://your-server-ip:8080 in your browser
  2. Log in with the username and password you configured in the credentials
  3. You’ll see the interactive timeline — start tracking time by clicking the start button or dragging on the timeline
  4. Add tags to your time entries by typing #tagname in the description

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 VariableDefaultPurpose
TIMETAGGER_BIND127.0.0.1:8080Server address and port
TIMETAGGER_DATADIR~/_timetaggerSQLite database location
TIMETAGGER_LOG_LEVELinfoLogging verbosity
TIMETAGGER_CREDENTIALS(none)User authentication
TIMETAGGER_PATH_PREFIX/timetagger/URL path prefix
TIMETAGGER_APP_REDIRECTfalseRedirect 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:

FieldValue
Domaintime.yourdomain.com
Schemehttp
Forward Hostnameyour-server-ip
Forward Port8080
SSLRequest 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

ResourceUsage
RAM50-100 MB idle, 150 MB under load
CPUMinimal — 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.

Comments