../../_images/kup6s-icon-deployment.svg

Solidtime time tracking

Self-hosted Solidtime (AGPL-v3, Laravel/Vue) time tracker deployed at time.kup.tirol. It replaces the paid Clockify SaaS.

Overview

Solidtime ships a single Docker image (solidtime/solidtime) run in three roles selected by the CONTAINER_MODE environment variable. You deploy these as three native CDK8S deployments plus a Gotenberg sidecar for PDF and report export.

Role

CONTAINER_MODE

Replicas

Purpose

Web

http

2

Laravel/Octane (FrankenPHP) web app on port 8000

Scheduler

scheduler

1

Laravel scheduler; also runs migrations (AUTO_DB_MIGRATE=true)

Worker

worker

1

Queue worker (WORKER_COMMAND); processes mail and jobs

Gotenberg

1

Stateless PDF rendering for report export

Cache, queue, and sessions use the Laravel database driver on CloudNativePG. There is no Redis.

Access

Open the app at https://time.kup.tirol. Registration is invite-only (APP_ENABLE_REGISTRATION=false). The super-admin is jk@kleinundpartner.at (SUPER_ADMINS). Invite new members from the Members UI — see How to manage members.

Architecture

Concern

Implementation

Source

dp-infra/solidtime/ (CDK8S, TypeScript), ArgoCD-managed

Database

CloudNativePG solidtime-postgres (PG 18, 2 instances, longhorn)

Database backups

barman-cloud plugin to backups-solidtime-kup6s (fsn1, cold), daily plus WAL

App file storage

Hetzner S3 solidtime-files-kup6s (hel1, hot, near the EX workers)

Secrets

ESO from application-secrets (app, SMTP) plus cluster-wide hetzner-s3-creds

Mail

Mailjet (in-v3.mailjet.com:587), from noreply@kleinundpartner.at

Ingress

Traefik plus cert-manager (letsencrypt-cluster-issuer), TLS solidtime-tls

Auth

Laravel Passport; APP_KEY plus a Passport RSA keypair in solidtime-app-secrets

DNS

time.kup.tirol.  IN  CNAME  lb.kup6s.net.

The CNAME to lb.kup6s.net reuses the LoadBalancer indirection, so a LoadBalancer IP change needs no DNS edit.

S3 buckets

The deployment uses two buckets in two regions.

Bucket

Region

Crossplane ProviderConfig

Use

solidtime-files-kup6s

hel1

hetzner-s3-hel1

App files (hot)

backups-solidtime-kup6s

fsn1

hetzner-s3

Database backups (cold)

The app reads S3 from S3_* environment variables, not AWS_*. The CNPG barman ObjectStore uses the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY secret keys.

Deploy

cd dp-infra/solidtime
pnpm run build
git add manifests/ config.yaml && git commit && git push

pnpm run build runs tsc and cdk8s synth to regenerate manifests/solidtime.k8s.yaml. ArgoCD auto-syncs on push to main.

Important

A change to a config.yaml value that lands in the solidtime-env ConfigMap (for example APP_ENABLE_REGISTRATION) does not restart the running pods. The envFrom reference is read only at pod start. After ArgoCD syncs the ConfigMap, run kubectl rollout restart deploy/solidtime-http deploy/solidtime-scheduler deploy/solidtime-worker -n solidtime. A change to the pod template (environment variable names, image tag) triggers a rollout automatically.

Health checks

kubectl get pods -n solidtime
kubectl get cluster.postgresql.cnpg.io solidtime-postgres -n solidtime
kubectl get certificate solidtime-tls -n solidtime
curl -sSI https://time.kup.tirol/login