360 words
2 minutes
Self-Hosted Donetick Setup with Docker Compose

Self-Hosted Donetick Setup with Docker Compose#

Simple guide to installing Donetick, a self-hosted task management app, using Docker Compose. Learn how to deploy Donetick and configure JWT authentication.

Create docker-compose.yml:

nano docker-compose.yml
services:
  donetick:
    image: donetick/donetick:v0.1.79
    container_name: donetick
    restart: unless-stopped
    ports:
      - 127.0.0.1:2021:2021
    volumes:
      - ./data:/donetick-data
      - ./config:/config
      - /usr/share/zoneinfo:/usr/share/zoneinfo:ro
    environment:
      - DT_ENV=selfhosted
      - DT_SQLITE_PATH=/donetick-data/donetick.db
      - TZ=Etc/UTC
    healthcheck:
      test: wget --no-verbose --tries=1 --spider http://localhost:2021/api/v1/health || exit 1
      start_period: 1m
      timeout: 5s
      interval: 1m
      retries: 3

Create config/selfhosted.yaml:

mkdir config
cd config
nano selfhosted.yaml
name: "selfhosted"
is_done_tick_dot_com: false
is_user_creation_disabled: false
telegram:
  token: ""
pushover:
  token: ""
database:
  type: "sqlite"
  migration: true
  # these are only required for postgres
  host: "secret"
  port: 5432
  user: "secret"
  password: "secret"
  name: "secret"
jwt:
  secret: "replace with 32-character random string"
  session_time: 168h # 7 days
  max_refresh: 1440h # 60 days 
server:
  port: 2021
  read_timeout: 10s
  write_timeout: 10s
  rate_period: 60s
  rate_limit: 300
  cors_allow_origins:
    - "http://localhost:5173"
    - "http://localhost:7926"
    # the below are required for the android app to work
    - "https://localhost"
    - "http://localhost"
    - "capacitor://localhost"
  serve_frontend: true
  serve_swagger: true
  public_host: "" # example: https://yourpublicdomain.com
logging:
  level: "info"
  encoding: "json"
  development: false
scheduler_jobs:
  due_job: 30m
  overdue_job: 3h
  pre_due_job: 3h
email:
  host:
  port:
  key:
  email:
  user: # optional, will be filled with email.email if not set.
  appHost:
  log_raw_url: true # log password reset URL to console (useful when email is not configured)
oauth2:
  client_id:
  client_secret:
  auth_url:
  token_url:
  user_info_url:
  redirect_url:
  name:
  # OIDC group-based role mapping (optional).
  # Users in these groups get the corresponding role on every OIDC login.
  # admin_groups:
  #   - "donetick-admins"
  # manager_groups:
  #   - "donetick-managers"
# Single-circle instance mode (optional).
# When true, disables circle join/leave endpoints and adds new OIDC users
# to a shared household circle instead of creating personal circles.
# single_circle_instance: false
# SSO-only mode (optional).
# When true, all password-based auth (signup, login, reset) is rejected and
# the login UI hides the password form.
# disable_password_auth: false
# Real-time configuration
realtime:
  enabled: true
  sse_enabled: true
  heartbeat_interval: 60s
  connection_timeout: 120s
  max_connections: 1000
  max_connections_per_user: 5
  event_queue_size: 2048
  cleanup_interval: 2m
  stale_threshold: 5m
  enable_compression: true
  enable_stats: true
  allowed_origins:
    - "*"

Set a strong secret for secret: "replace with 32-character random string".

Run Docker:

docker compose up -d

Open a web browser and go to http://127.0.0.1:2021.

Self-Hosted Donetick Setup with Docker Compose
https://blog.juyung.com/posts/en/2026/09/20/
Author
Jay Juyung
Published at
2026-09-20
License
CC BY