Skip to content

Latest commit

 

History

History
96 lines (67 loc) · 7.45 KB

RENDER.md

File metadata and controls

96 lines (67 loc) · 7.45 KB

Deploy TimeTracker on Render

This guide explains how to host TimeTracker as a Web Service on Render with optional demo mode (single user, credentials shown on the login page).

The Blueprint uses the pre-built Docker image from GitHub Container Registry (ghcr.io/drytrix/timetracker:latest), i.e. the same image built by your GitHub Actions (cd-release / cd-development). No Python or npm build runs on Render.

Prerequisites

  • A Render account
  • This repository connected to your GitHub (or GitLab) account
  • Docker image published to GHCR (e.g. by pushing to main or creating a release so the CD workflow builds and pushes the image)

Deploy with the Blueprint

  1. In the Render Dashboard, click New → Blueprint.
  2. Connect your Git provider and select the TimeTracker repository.
  3. Render will detect the render.yaml in the repository root.
  4. Review the blueprint: it creates one PostgreSQL database and one Web Service that pulls ghcr.io/drytrix/timetracker:latest.
  5. Click Apply to create the database and deploy the app.

To deploy a new version, push to main or create a release so GitHub Actions builds and pushes a new image, then in Render trigger a Manual Deploy (or use a deploy hook) to pull the updated image.

Automatic demo deploy via release workflow

If you host a demo site on Render, the release workflow can automatically trigger a redeploy when a new container is published. Configure:

  1. In Render: open your demo Web Service → Settings → Deploy Hook and copy the deploy hook URL.
  2. In GitHub: add an organization secret named TimeTrackerDemoRender with the deploy hook URL as the value. Grant your TimeTracker repository access to this secret (Organization Settings → Secrets and variables → Actions → Repository access).
  3. On each release (push to main, tag, or manual dispatch), after the Docker image is built and pushed, the workflow sends a POST request to the deploy hook. Render will pull the new image and redeploy your demo site.

If the secret is not set, the workflow skips the deploy trigger and the release completes normally.

Environment variables

The blueprint sets:

  • FLASK_ENV: production
  • FLASK_APP: app:create_app() (used for flask db upgrade in the pre-deploy step)
  • SECRET_KEY: Auto-generated by Render (recommended; you can override in the Dashboard)
  • DATABASE_URL: Filled automatically from the linked PostgreSQL database
  • AUTH_METHOD: local (default), or none | oidc | ldap | both | all — see env.example and LDAP Setup / OIDC Setup
  • REDIS_ENABLED: false (rate limiting uses in-memory storage; no Redis required for demo)

Optional (usually leave unset on Render):

  • GUNICORN_MAX_REQUESTS: Worker recycle after N requests. Defaults to 0 (disabled) when PORT is set. A single eventlet worker cannot recycle without a brief no-listener window; leave disabled unless you need recycle for memory hygiene.
  • GUNICORN_MAX_REQUESTS_JITTER: Jitter added to max-requests when recycle is enabled (default 100).

Demo mode (single-user demo)

To run a demo instance where only one user can log in and the credentials are shown on the login page:

  1. In the Render Dashboard, open your timetracker web service.
  2. Go to Environment and add (or uncomment in render.yaml and redeploy):
    • DEMO_MODE: true
    • DEMO_USERNAME: demo (or any username you want)
    • DEMO_PASSWORD: Set a strong password (use a Secret so it is not visible in the dashboard to others)
  3. Redeploy the service.

In demo mode:

  • Only the user with DEMO_USERNAME can log in.
  • The login page shows the demo username and password.
  • Self-registration and admin user creation are disabled; OIDC cannot create new users.
  • The demo user is created automatically on first run if it does not exist.
  • The first-run /setup wizard is skipped; setup is marked complete automatically.
  • On first boot (empty DB), sample clients, projects, and time entries are seeded so the dashboard is browseable.
  • The demo account has the standard user role only (not an administrator), so settings, PDF layout editing, and similar admin routes are unavailable on the demo login.

Public demo (this project): https://timetracker-demo.drytrix.com — username test, password test123!.

Security: Use a strong DEMO_PASSWORD for any public demo. Do not use DEMO_MODE=true for production multi-user deployments.

Upgrading older demos: If your database was created before this change and the demo user still has admin access, redeploying a current image runs a one-time adjustment on startup: the demo user is moved to the user role and admin / super_admin RBAC roles are removed. If you disabled DEMO_MODE and need a real administrator, create one via CLI or temporarily disable demo mode and use ADMIN_USERNAMES as documented in the main README.

Optional: Run without the Blueprint

If you prefer to create the database and web service manually:

  1. Create a PostgreSQL database and note the Internal Database URL (or External if your app runs elsewhere).
  2. Create a Web Service and choose Deploy an existing image from a registry.
    • Image URL: ghcr.io/drytrix/timetracker:latest
    • Docker Command: gunicorn --bind 0.0.0.0:$PORT --worker-class eventlet --workers 1 --timeout 120 "app:create_app()"
    • Pre-deploy Command: flask db upgrade
    • If the image is private, add GHCR registry credentials in Render (Settings → Registry).
  3. In Environment, set FLASK_APP to app:create_app(), DATABASE_URL to the Postgres URL, SECRET_KEY, and any demo-mode variables as above.

Troubleshooting

  • Migrations: The pre-deploy command runs flask db upgrade. If it fails, check that FLASK_APP is set to app:create_app() and that DATABASE_URL is set and reachable from Render.
  • Image: The Blueprint uses the pre-built image ghcr.io/drytrix/timetracker:latest. Ensure that image exists (trigger a build by pushing to main or running the release workflow). If the image is private, add GitHub Container Registry credentials in Render Dashboard → Settings → Registry.
  • Database URL: Render’s PostgreSQL URL is usually in postgres:// form; the app uses postgresql+psycopg2. If you see connection errors, try setting DATABASE_URL to the same URL with the scheme changed to postgresql:// (Render may also provide a direct URL in the database dashboard).
  • Server failure / connection refused on /_health: This is not rate limiting. Rate limits return HTTP 429; dial tcp … connection refused means nothing was listening (process restarting, recycling, OOM-killed, or free-tier spin-down). Confirm:
    • Health Check Path is /_health (liveness only; do not use /_ready as Render’s kill/replace probe — DB blips would restart the instance).
    • The running image includes health-probe rate-limit exemptions (/_health, /_ready, /api/health, /api/v1/health).
    • Worker recycle is off by default on Render (PORT is set → GUNICORN_MAX_REQUESTS defaults to 0). Set GUNICORN_MAX_REQUESTS=1000 (and optionally GUNICORN_MAX_REQUESTS_JITTER) only if you explicitly want Gunicorn to recycle workers; with a single eventlet worker that can briefly refuse connections and trip this alert.