# Dokku deployment App: `stride`; database: `stride-db`; domain: `stride.peterstockings.com`. The Dockerfile installs locked dependencies on Python 3.12. Dokku runs database migrations in the predeploy step, then one web container and one sync worker. Mount `/var/lib/dokku/data/storage/stride:/storage` with ownership 1000:1000 and mode 0700. Set APP_ENV=production, INSTANCE_PATH=/storage/instance, DATA_ROOT=/storage/data, SECRET_KEY (random secret), TOKEN_ENCRYPTION_KEY (Fernet key). Linking PostgreSQL supplies DATABASE_URL. Preserve the encryption key separately from database backups; losing it requires Garmin reconnection. Push to Gitea, then deploy with `dokku git:sync --build stride https://gitea.peterstockings.com/peterstockings/Stride.git main` on the server. Enable HTTPS using `dokku letsencrypt:enable stride`. ## Sync Upload a garminconnect 0.3.17 session JSON in Settings & imports. Credentials are validated with Garmin, encrypted, and assigned to the authenticated owner. Manual sync queues durable work; the worker polls every 30 seconds. Automatic sync runs hourly and checks the latest 100 activities, reusing download manifests and unique activity source keys to avoid duplicates. Older activities can be imported with FIT uploads or the operator's local import command. Shared storage file locks prevent overlapping jobs and disconnect races. A crash releases the lock; due or pending work is retried after restart. Refresh tokens are persisted before downloading and after download failures. Transient failures back off up to six hours; rejected authentication pauses scheduling until the user reconnects and enables it again. Disconnect removes credentials and keeps previously imported activities. Only garmin.com is supported by the web connector. Inspect `dokku logs stride -p worker`, the status in Settings, and `/health`. Run `dokku run stride flask --app app sync-worker --once` for an operator check. Do not run the local sync simultaneously using a copy of the same Garmin session. Back up PostgreSQL, `/storage`, and configuration securely. Rate limiting still uses per-process memory; email verification and password recovery are not yet implemented. For a larger public deployment configure shared rate-limit storage. ## Validation Local automated suite: 39 passing tests, including manual/scheduled jobs, encryption, retry/authentication failure, locking, CSRF, and access control. Live deployment and Garmin results will be recorded after verification.