TimeTracker logo

TimeTracker

Self-hosted time tracking and invoicing for freelancers and teams

Alternative to: toggl, harvest, clockify


TimeTracker is a self-hosted, web-based time tracking application built for freelancers, teams, and agencies. It combines timers and project/task management with a Kanban board, client invoicing with PDF and Peppol/ZUGFeRD e-invoicing, expense tracking, and detailed reports, plus OIDC/SSO login and a REST API.

TimeTracker Docker Compose example

Self-host TimeTracker on your own server, homelab, or VPS starting from this Docker Compose example. It runs TimeTracker in Docker containers using the official drytrix/timetracker:latest, postgres:16-alpine images, with persistent volumes and automatic restarts preconfigured. Review the environment variables and adjust them to your setup, save the file as compose.yml (or docker-compose.yml), and start the stack with docker compose up -d.

services:
  app:
    image: drytrix/timetracker:latest
    restart: unless-stopped
    environment:
      # Timezone used by the application.
      TZ: "UTC"
      # Default currency for invoices and reports (ISO 4217 code).
      CURRENCY: "EUR"
      # Comma-separated usernames that are granted admin rights.
      ADMIN_USERNAMES: "admin"
      # Allow visitors to create their own accounts (true/false).
      ALLOW_SELF_REGISTER: "false"
      # Secret key used to sign sessions and CSRF tokens. Use a random 32+ character string.
      SECRET_KEY: "3bd99edcb83dfb603ea4ba585a3e3f49c8c886fb6f7234d94a57a6239510970f"
      # Public URL where the app is reached (e.g. https://time.example.com).
      APP_BASE_URL: "changeme"
      # Comma-separated origins trusted for CSRF-protected form submissions; include your public URL.
      WTF_CSRF_TRUSTED_ORIGINS: "changeme"
      # Scheme used when generating absolute URLs (https behind a TLS-terminating reverse proxy).
      PREFERRED_URL_SCHEME: "https"
      # Connection string for the PostgreSQL database (internal service).
      DATABASE_URL: "postgresql+psycopg2://timetracker:timetracker@db:5432/timetracker"
      # Redis is optional and disabled by default (sessions are stored server-side).
      REDIS_ENABLED: "false"
    volumes:
      - app_data:/data
      - app_logs:/app/logs
      - app_uploads:/app/app/static/uploads
    depends_on:
      db:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "-s", "-o", "/dev/null", "http://localhost:8080/_health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    environment:
      # Database name created on first boot.
      POSTGRES_DB: "timetracker"
      # Database user created on first boot.
      POSTGRES_USER: "timetracker"
      # Database password (internal to the compose network).
      POSTGRES_PASSWORD: "timetracker"
    volumes:
      - db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U timetracker -d timetracker"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s

volumes:
  app_data:
  app_logs:
  app_uploads:
  db_data:

Values set to changeme are required — replace them with your own values before starting TimeTracker.

Prefer a managed setup? WinterFlow installs, configures, and updates TimeTracker for you using this same Docker Compose configuration.