From 8d7ecb65b39b199712a53c7f95715a7aa37a2b16 Mon Sep 17 00:00:00 2001 From: Shifra Williams Date: Fri, 8 May 2026 17:18:04 -0700 Subject: [PATCH 1/6] chore: remove emoji --- README.md | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index a9049b4779..e3dedeb975 100644 --- a/README.md +++ b/README.md @@ -6,25 +6,25 @@ ## Technology Stack and Features -- โšก [**FastAPI**](https://fastapi.tiangolo.com) for the Python backend API. - - ๐Ÿงฐ [SQLModel](https://sqlmodel.tiangolo.com) for the Python SQL database interactions (ORM). - - ๐Ÿ” [Pydantic](https://docs.pydantic.dev), used by FastAPI, for the data validation and settings management. - - ๐Ÿ’พ [PostgreSQL](https://www.postgresql.org) as the SQL database. -- ๐Ÿš€ [React](https://react.dev) for the frontend. - - ๐Ÿ’ƒ Using TypeScript, hooks, [Vite](https://vitejs.dev), and other parts of a modern frontend stack. - - ๐ŸŽจ [Tailwind CSS](https://tailwindcss.com) and [shadcn/ui](https://ui.shadcn.com) for the frontend components. - - ๐Ÿค– An automatically generated frontend client. - - ๐Ÿงช [Playwright](https://playwright.dev) for End-to-End testing. - - ๐Ÿฆ‡ Dark mode support. -- ๐Ÿ‹ [Docker Compose](https://www.docker.com) for development and production. -- ๐Ÿ”’ Secure password hashing by default. -- ๐Ÿ”‘ JWT (JSON Web Token) authentication. -- ๐Ÿ“ซ Email based password recovery. -- ๐Ÿ“ฌ [Mailcatcher](https://mailcatcher.me) for local email testing during development. -- โœ… Tests with [Pytest](https://pytest.org). -- ๐Ÿ“ž [Traefik](https://traefik.io) as a reverse proxy / load balancer. -- ๐Ÿšข Deployment instructions using Docker Compose, including how to set up a frontend Traefik proxy to handle automatic HTTPS certificates. -- ๐Ÿญ CI (continuous integration) and CD (continuous deployment) based on GitHub Actions. +- [**FastAPI**](https://fastapi.tiangolo.com) for the Python backend API. + - [SQLModel](https://sqlmodel.tiangolo.com) for the Python SQL database interactions (ORM). + - [Pydantic](https://docs.pydantic.dev), used by FastAPI, for the data validation and settings management. + - [PostgreSQL](https://www.postgresql.org) as the SQL database. +- [React](https://react.dev) for the frontend. + - Using TypeScript, hooks, [Vite](https://vitejs.dev), and other parts of a modern frontend stack. + - [Tailwind CSS](https://tailwindcss.com) and [shadcn/ui](https://ui.shadcn.com) for the frontend components. + - An automatically generated frontend client. + - [Playwright](https://playwright.dev) for End-to-End testing. + - Dark mode support. +- [Docker Compose](https://www.docker.com) for development and production. +- Secure password hashing by default. +- JWT (JSON Web Token) authentication. +- Email based password recovery. +- [Mailcatcher](https://mailcatcher.me) for local email testing during development. +- Tests with [Pytest](https://pytest.org). +- [Traefik](https://traefik.io) as a reverse proxy / load balancer. +- Deployment instructions using Docker Compose, including how to set up a frontend Traefik proxy to handle automatic HTTPS certificates. +- CI (continuous integration) and CD (continuous deployment) based on GitHub Actions. ### Dashboard Login From d5b472d808a9d41fda63134ff4343af711b5acec Mon Sep 17 00:00:00 2001 From: Ho1yShif Date: Fri, 8 May 2026 20:34:38 -0700 Subject: [PATCH 2/6] chore: update repository links in README and render.yaml to point to render-examples --- README.md | 27 +++++++++++++++ render.yaml | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 render.yaml diff --git a/README.md b/README.md index e3dedeb975..7f80489896 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,33 @@ Test Backend Coverage +## Deploy to Render + +[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/render-examples/full-stack-fastapi-template) + +One click provisions the full stack on [Render](https://render.com) using the [`render.yaml`](./render.yaml) Blueprint at the repo root: a Dockerized FastAPI web service, a Vite/Bun static site for the frontend, a managed Postgres database, and a shared `fastapi-env` environment-variable group that both services pull from. + +### Setup after clicking the button + +1. **Fill in the placeholder values in the `fastapi-env` env group** (Dashboard โ†’ Env Groups โ†’ `fastapi-env`): + - `FIRST_SUPERUSER` โ€” admin email, e.g. `admin@yourdomain.com` + - `FIRST_SUPERUSER_PASSWORD` โ€” generate one with `python -c "import secrets; print(secrets.token_urlsafe(32))"` + - `DOMAIN` โ€” your custom domain, or any placeholder if you'll use the `.onrender.com` URLs + - Optional: `SMTP_HOST` / `SMTP_USER` / `SMTP_PASSWORD` / `EMAILS_FROM_EMAIL` (if you want password-reset emails) and `SENTRY_DSN` (if using Sentry) + + `SECRET_KEY`, all `POSTGRES_*` vars, `ENVIRONMENT`, `PROJECT_NAME`, `STACK_NAME`, and the `SMTP_TLS/SSL/PORT` defaults are all set automatically โ€” no action needed. + + > **Note:** the `POSTGRES_*` vars are wired directly to the `fastapi-backend` service via `fromDatabase` references (Render's env groups don't support database links), so they won't show up on the `fastapi-env` group page. You'll find them under `fastapi-backend` โ†’ Environment in the Dashboard, alongside the inherited group vars. They all end up in the same `os.environ` at runtime โ€” the split is purely a Dashboard organizational thing. + +2. **Wait for both services to finish deploying** so they're assigned `.onrender.com` URLs. + +3. **Go back to the `fastapi-env` env group and fill in the cross-service URLs** (these can only be set after the first deploy, since they depend on the assigned hostnames). All three live in the env group, but they're consumed by different services: + - `VITE_API_URL` โ†’ set to the **backend** URL, e.g. `https://fastapi-backend-XXXX.onrender.com`. *Read by the **frontend** service at build time โ€” baked into the bundle.* + - `FRONTEND_HOST` โ†’ set to the **frontend** URL, e.g. `https://fastapi-frontend-XXXX.onrender.com`. *Read by the **backend** service at runtime โ€” auto-appended to the CORS allowlist.* + - `BACKEND_CORS_ORIGINS` โ†’ comma-separated list of additional allowed origins (you can usually leave this empty since `FRONTEND_HOST` already covers the frontend). *Read by the **backend** service at runtime.* + +4. **Trigger a manual rebuild of the frontend** (Dashboard โ†’ `fastapi-frontend` โ†’ Manual Deploy โ†’ Clear build cache & deploy). `VITE_API_URL` is baked into the bundle at build time, so an existing build won't pick up the new value until rebuilt. + ## Technology Stack and Features - [**FastAPI**](https://fastapi.tiangolo.com) for the Python backend API. diff --git a/render.yaml b/render.yaml new file mode 100644 index 0000000000..5f795df341 --- /dev/null +++ b/render.yaml @@ -0,0 +1,98 @@ +envVarGroups: + - name: fastapi-env + envVars: + - key: ENVIRONMENT + value: production + - key: PROJECT_NAME + value: "Full Stack FastAPI" + - key: STACK_NAME + value: fastapi-project + - key: SMTP_TLS + value: "true" + - key: SMTP_SSL + value: "false" + - key: SMTP_PORT + value: "587" + - key: SECRET_KEY + generateValue: true + - key: FIRST_SUPERUSER + sync: false + - key: FIRST_SUPERUSER_PASSWORD + sync: false + - key: DOMAIN + sync: false + - key: FRONTEND_HOST + sync: false + - key: BACKEND_CORS_ORIGINS + sync: false + - key: SMTP_HOST + sync: false + - key: SMTP_USER + sync: false + - key: SMTP_PASSWORD + sync: false + - key: EMAILS_FROM_EMAIL + sync: false + - key: SENTRY_DSN + sync: false + - key: VITE_API_URL + sync: false + +projects: + - name: full-stack-fastapi-template + environments: + - name: production + services: + - type: web + name: fastapi-backend + runtime: docker + plan: free + repo: https://github.com/render-examples/full-stack-fastapi-template + branch: main + autoDeploy: true + dockerfilePath: ./backend/Dockerfile + dockerContext: . + dockerCommand: bash scripts/start.sh + healthCheckPath: /api/v1/utils/health-check/ + envVars: + - fromGroup: fastapi-env + - key: POSTGRES_SERVER + fromDatabase: + name: fastapi-db + property: host + - key: POSTGRES_PORT + fromDatabase: + name: fastapi-db + property: port + - key: POSTGRES_USER + fromDatabase: + name: fastapi-db + property: user + - key: POSTGRES_PASSWORD + fromDatabase: + name: fastapi-db + property: password + - key: POSTGRES_DB + fromDatabase: + name: fastapi-db + property: database + + - type: web + name: fastapi-frontend + runtime: static + repo: https://github.com/render-examples/full-stack-fastapi-template + branch: main + autoDeploy: true + buildCommand: bun install --frozen-lockfile && bun run --filter frontend build + staticPublishPath: frontend/dist + routes: + - type: rewrite + source: /* + destination: /index.html + envVars: + - fromGroup: fastapi-env + + databases: + - name: fastapi-db + databaseName: app + plan: free From 7be74a3dda787913dba66b7253e818bd03971769 Mon Sep 17 00:00:00 2001 From: Ho1yShif Date: Fri, 8 May 2026 20:48:50 -0700 Subject: [PATCH 3/6] fix(render): move secrets out of env group to service envVars Render's envVarGroups don't accept sync: false placeholders, which silently dropped FIRST_SUPERUSER, FIRST_SUPERUSER_PASSWORD, FRONTEND_HOST, BACKEND_CORS_ORIGINS, SMTP credentials, EMAILS_FROM_EMAIL, SENTRY_DSN, and VITE_API_URL on apply. Move each to its consuming service (backend or frontend) and keep the env group for shared, non-secret defaults only. Also move SECRET_KEY (backend-only) to the backend service and drop the unused DOMAIN variable. README setup steps updated to point users at the correct service Environment pages. Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 15 +++++++-------- render.yaml | 47 ++++++++++++++++++++++------------------------- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 7f80489896..22165dd678 100644 --- a/README.md +++ b/README.md @@ -12,22 +12,21 @@ One click provisions the full stack on [Render](https://render.com) using the [` ### Setup after clicking the button -1. **Fill in the placeholder values in the `fastapi-env` env group** (Dashboard โ†’ Env Groups โ†’ `fastapi-env`): +1. **Fill in the secret env vars on `fastapi-backend`** (Dashboard โ†’ `fastapi-backend` โ†’ Environment): - `FIRST_SUPERUSER` โ€” admin email, e.g. `admin@yourdomain.com` - `FIRST_SUPERUSER_PASSWORD` โ€” generate one with `python -c "import secrets; print(secrets.token_urlsafe(32))"` - - `DOMAIN` โ€” your custom domain, or any placeholder if you'll use the `.onrender.com` URLs - Optional: `SMTP_HOST` / `SMTP_USER` / `SMTP_PASSWORD` / `EMAILS_FROM_EMAIL` (if you want password-reset emails) and `SENTRY_DSN` (if using Sentry) - `SECRET_KEY`, all `POSTGRES_*` vars, `ENVIRONMENT`, `PROJECT_NAME`, `STACK_NAME`, and the `SMTP_TLS/SSL/PORT` defaults are all set automatically โ€” no action needed. + `SECRET_KEY` and all `POSTGRES_*` vars are set automatically. `ENVIRONMENT`, `PROJECT_NAME`, `STACK_NAME`, and the `SMTP_TLS/SSL/PORT` defaults come from the `fastapi-env` env group โ€” no action needed. - > **Note:** the `POSTGRES_*` vars are wired directly to the `fastapi-backend` service via `fromDatabase` references (Render's env groups don't support database links), so they won't show up on the `fastapi-env` group page. You'll find them under `fastapi-backend` โ†’ Environment in the Dashboard, alongside the inherited group vars. They all end up in the same `os.environ` at runtime โ€” the split is purely a Dashboard organizational thing. + > **Note:** secrets live on the service (not the env group) because Render's env groups don't accept `sync: false` placeholders. The `fastapi-env` group only holds shared, non-secret defaults; everything secret or service-specific is set directly on `fastapi-backend` or `fastapi-frontend`. 2. **Wait for both services to finish deploying** so they're assigned `.onrender.com` URLs. -3. **Go back to the `fastapi-env` env group and fill in the cross-service URLs** (these can only be set after the first deploy, since they depend on the assigned hostnames). All three live in the env group, but they're consumed by different services: - - `VITE_API_URL` โ†’ set to the **backend** URL, e.g. `https://fastapi-backend-XXXX.onrender.com`. *Read by the **frontend** service at build time โ€” baked into the bundle.* - - `FRONTEND_HOST` โ†’ set to the **frontend** URL, e.g. `https://fastapi-frontend-XXXX.onrender.com`. *Read by the **backend** service at runtime โ€” auto-appended to the CORS allowlist.* - - `BACKEND_CORS_ORIGINS` โ†’ comma-separated list of additional allowed origins (you can usually leave this empty since `FRONTEND_HOST` already covers the frontend). *Read by the **backend** service at runtime.* +3. **Set the cross-service URLs on the appropriate services** (these can only be set after the first deploy, since they depend on the assigned hostnames): + - `VITE_API_URL` โ†’ set on **`fastapi-frontend`** โ†’ Environment, to the **backend** URL, e.g. `https://fastapi-backend-XXXX.onrender.com`. *Baked into the frontend bundle at build time.* + - `FRONTEND_HOST` โ†’ set on **`fastapi-backend`** โ†’ Environment, to the **frontend** URL, e.g. `https://fastapi-frontend-XXXX.onrender.com`. *Auto-appended to the backend's CORS allowlist at runtime.* + - `BACKEND_CORS_ORIGINS` โ†’ set on **`fastapi-backend`** โ†’ Environment, comma-separated list of additional allowed origins (you can usually leave this empty since `FRONTEND_HOST` already covers the frontend). 4. **Trigger a manual rebuild of the frontend** (Dashboard โ†’ `fastapi-frontend` โ†’ Manual Deploy โ†’ Clear build cache & deploy). `VITE_API_URL` is baked into the bundle at build time, so an existing build won't pick up the new value until rebuilt. diff --git a/render.yaml b/render.yaml index 5f795df341..c345b39dc0 100644 --- a/render.yaml +++ b/render.yaml @@ -13,30 +13,6 @@ envVarGroups: value: "false" - key: SMTP_PORT value: "587" - - key: SECRET_KEY - generateValue: true - - key: FIRST_SUPERUSER - sync: false - - key: FIRST_SUPERUSER_PASSWORD - sync: false - - key: DOMAIN - sync: false - - key: FRONTEND_HOST - sync: false - - key: BACKEND_CORS_ORIGINS - sync: false - - key: SMTP_HOST - sync: false - - key: SMTP_USER - sync: false - - key: SMTP_PASSWORD - sync: false - - key: EMAILS_FROM_EMAIL - sync: false - - key: SENTRY_DSN - sync: false - - key: VITE_API_URL - sync: false projects: - name: full-stack-fastapi-template @@ -56,6 +32,8 @@ projects: healthCheckPath: /api/v1/utils/health-check/ envVars: - fromGroup: fastapi-env + - key: SECRET_KEY + generateValue: true - key: POSTGRES_SERVER fromDatabase: name: fastapi-db @@ -76,6 +54,24 @@ projects: fromDatabase: name: fastapi-db property: database + - key: FIRST_SUPERUSER + sync: false + - key: FIRST_SUPERUSER_PASSWORD + sync: false + - key: FRONTEND_HOST + sync: false + - key: BACKEND_CORS_ORIGINS + sync: false + - key: SMTP_HOST + sync: false + - key: SMTP_USER + sync: false + - key: SMTP_PASSWORD + sync: false + - key: EMAILS_FROM_EMAIL + sync: false + - key: SENTRY_DSN + sync: false - type: web name: fastapi-frontend @@ -90,7 +86,8 @@ projects: source: /* destination: /index.html envVars: - - fromGroup: fastapi-env + - key: VITE_API_URL + sync: false databases: - name: fastapi-db From 58a36a08cb06badfe5ee4c32e23871e7501ecd33 Mon Sep 17 00:00:00 2001 From: Ho1yShif Date: Thu, 28 May 2026 23:08:20 -0700 Subject: [PATCH 4/6] fix(render): restore start.sh removed upstream Upstream deleted backend/scripts/start.sh and split its responsibilities: the Dockerfile CMD now runs fastapi directly, and prestart.sh runs in a separate compose service. Render has no equivalent of "wait for sidecar to complete," and the existing Render service is configured with `bash scripts/start.sh` as its Docker command. Restore the file with its prior contents so the deploy works without any Dashboard config changes. Co-Authored-By: Claude Opus 4.7 (1M context) --- backend/scripts/start.sh | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 backend/scripts/start.sh diff --git a/backend/scripts/start.sh b/backend/scripts/start.sh new file mode 100644 index 0000000000..5715eb21dc --- /dev/null +++ b/backend/scripts/start.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -e +bash scripts/prestart.sh +exec fastapi run --workers 4 --host 0.0.0.0 --port "$PORT" app/main.py From 40d67481ff89b3dd3b79b06b4daf68bb55d63285 Mon Sep 17 00:00:00 2001 From: Ho1yShif Date: Thu, 28 May 2026 23:14:42 -0700 Subject: [PATCH 5/6] fix(render): honor WEB_CONCURRENCY for worker count MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Render auto-sets WEB_CONCURRENCY based on the instance's CPU/memory (1 on the free tier). Hardcoding --workers 4 caused workers to be OOM-killed on the free tier โ€” the parent kept respawning replacements, producing a steady stream of "Child process died" messages and wasted resources. Default to 1 if WEB_CONCURRENCY is unset. Co-Authored-By: Claude Opus 4.7 (1M context) --- backend/scripts/start.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/scripts/start.sh b/backend/scripts/start.sh index 5715eb21dc..4079c0c4c0 100644 --- a/backend/scripts/start.sh +++ b/backend/scripts/start.sh @@ -1,4 +1,4 @@ #!/usr/bin/env bash set -e bash scripts/prestart.sh -exec fastapi run --workers 4 --host 0.0.0.0 --port "$PORT" app/main.py +exec fastapi run --workers "${WEB_CONCURRENCY:-1}" --host 0.0.0.0 --port "$PORT" app/main.py From f3822ff48a495ef581ea20157ae24c6178f003ec Mon Sep 17 00:00:00 2001 From: Ho1yShif Date: Thu, 28 May 2026 23:23:17 -0700 Subject: [PATCH 6/6] chore: point Deploy to Render URLs at fastapi/full-stack-fastapi-template MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For upstream PR โ€” if merged, the Blueprint lives in fastapi/..., so the deploy button and render.yaml repo: field should point there. Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 2 +- render.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 22165dd678..c9c37caa08 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ## Deploy to Render -[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/render-examples/full-stack-fastapi-template) +[![Deploy to Render](https://render.com/images/deploy-to-render-button.svg)](https://render.com/deploy?repo=https://github.com/fastapi/full-stack-fastapi-template) One click provisions the full stack on [Render](https://render.com) using the [`render.yaml`](./render.yaml) Blueprint at the repo root: a Dockerized FastAPI web service, a Vite/Bun static site for the frontend, a managed Postgres database, and a shared `fastapi-env` environment-variable group that both services pull from. diff --git a/render.yaml b/render.yaml index c345b39dc0..08eaaf4cd9 100644 --- a/render.yaml +++ b/render.yaml @@ -23,7 +23,7 @@ projects: name: fastapi-backend runtime: docker plan: free - repo: https://github.com/render-examples/full-stack-fastapi-template + repo: https://github.com/fastapi/full-stack-fastapi-template branch: main autoDeploy: true dockerfilePath: ./backend/Dockerfile @@ -76,7 +76,7 @@ projects: - type: web name: fastapi-frontend runtime: static - repo: https://github.com/render-examples/full-stack-fastapi-template + repo: https://github.com/fastapi/full-stack-fastapi-template branch: main autoDeploy: true buildCommand: bun install --frozen-lockfile && bun run --filter frontend build