mirror of
https://github.com/fastapi/full-stack-fastapi-template.git
synced 2026-11-03 20:36:27 +00:00
Compare commits
7 Commits
5217a7e693
..
0.11.1
| Author | SHA1 | Date | |
|---|---|---|---|
| ddb3c34f19 | |||
| eae2d6701d | |||
| 6b9fa4113c | |||
| b55b3122ab | |||
| 6dbb2e8256 | |||
| 6d75b2d132 | |||
| 32ab6dd022 |
@@ -14,7 +14,5 @@ SMTP_TLS=False
|
||||
SMTP_PORT=1025
|
||||
|
||||
# Postgres
|
||||
POSTGRES_SERVER=localhost
|
||||
POSTGRES_DB=app
|
||||
POSTGRES_USER=postgres
|
||||
POSTGRES_PASSWORD=changethis
|
||||
DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@localhost:5432/app
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
name: Deploy to Production
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- published
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
environment: production
|
||||
# Do not deploy in the main repository, only in user projects
|
||||
if: github.repository_owner != 'fastapi'
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- production
|
||||
env:
|
||||
DOMAIN: ${{ secrets.DOMAIN }}
|
||||
SECRET_KEY: ${{ secrets.SECRET_KEY }}
|
||||
FIRST_SUPERUSER: ${{ secrets.FIRST_SUPERUSER }}
|
||||
FIRST_SUPERUSER_PASSWORD: ${{ secrets.FIRST_SUPERUSER_PASSWORD }}
|
||||
SMTP_HOST: ${{ secrets.SMTP_HOST }}
|
||||
SMTP_USER: ${{ secrets.SMTP_USER }}
|
||||
SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }}
|
||||
EMAILS_FROM_EMAIL: ${{ secrets.EMAILS_FROM_EMAIL }}
|
||||
POSTGRES_PASSWORD: ${{ secrets.POSTGRES_PASSWORD }}
|
||||
SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
- run: docker compose -f compose.yml -f compose.deploy.yml build
|
||||
- run: docker compose -f compose.yml -f compose.deploy.yml run --rm backend bash scripts/prestart.sh
|
||||
- run: docker compose -f compose.yml -f compose.deploy.yml up -d
|
||||
@@ -1,21 +1,20 @@
|
||||
name: Deploy to Staging
|
||||
name: Deploy
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- release-notes.md
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
environment: staging
|
||||
# Do not deploy in the main repository, only in user projects
|
||||
if: github.repository_owner != 'fastapi'
|
||||
runs-on:
|
||||
- self-hosted
|
||||
- staging
|
||||
runs-on: self-hosted
|
||||
env:
|
||||
DOMAIN: ${{ secrets.DOMAIN }}
|
||||
SECRET_KEY: ${{ secrets.SECRET_KEY }}
|
||||
@@ -31,7 +31,7 @@ target_metadata = SQLModel.metadata
|
||||
|
||||
|
||||
def get_url():
|
||||
return str(settings.SQLALCHEMY_DATABASE_URI)
|
||||
return str(settings.DATABASE_URL)
|
||||
|
||||
|
||||
def run_migrations_offline():
|
||||
|
||||
+12
-17
@@ -6,6 +6,7 @@ from pydantic import (
|
||||
HttpUrl,
|
||||
PostgresDsn,
|
||||
computed_field,
|
||||
field_validator,
|
||||
model_validator,
|
||||
)
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
@@ -27,23 +28,16 @@ class Settings(BaseSettings):
|
||||
|
||||
PROJECT_NAME: str
|
||||
SENTRY_DSN: HttpUrl | None = None
|
||||
POSTGRES_SERVER: str
|
||||
POSTGRES_PORT: int = 5432
|
||||
POSTGRES_USER: str
|
||||
POSTGRES_PASSWORD: str = ""
|
||||
POSTGRES_DB: str = ""
|
||||
DATABASE_URL: PostgresDsn
|
||||
|
||||
@computed_field # type: ignore[prop-decorator]
|
||||
@property
|
||||
def SQLALCHEMY_DATABASE_URI(self) -> PostgresDsn:
|
||||
return PostgresDsn.build(
|
||||
scheme="postgresql+psycopg",
|
||||
username=self.POSTGRES_USER,
|
||||
password=self.POSTGRES_PASSWORD,
|
||||
host=self.POSTGRES_SERVER,
|
||||
port=self.POSTGRES_PORT,
|
||||
path=self.POSTGRES_DB,
|
||||
)
|
||||
@field_validator("DATABASE_URL", mode="before")
|
||||
@classmethod
|
||||
def _use_psycopg_driver(cls, value: str | PostgresDsn) -> str:
|
||||
database_url = str(value)
|
||||
for scheme in ("postgres://", "postgresql://"):
|
||||
if database_url.startswith(scheme):
|
||||
return database_url.replace(scheme, "postgresql+psycopg://", 1)
|
||||
return database_url
|
||||
|
||||
SMTP_TLS: bool = True
|
||||
SMTP_SSL: bool = False
|
||||
@@ -85,7 +79,8 @@ class Settings(BaseSettings):
|
||||
@model_validator(mode="after")
|
||||
def _enforce_non_default_secrets(self) -> Self:
|
||||
self._check_default_secret("SECRET_KEY", self.SECRET_KEY)
|
||||
self._check_default_secret("POSTGRES_PASSWORD", self.POSTGRES_PASSWORD)
|
||||
for host in self.DATABASE_URL.hosts():
|
||||
self._check_default_secret("DATABASE_URL password", host["password"])
|
||||
self._check_default_secret(
|
||||
"FIRST_SUPERUSER_PASSWORD", self.FIRST_SUPERUSER_PASSWORD
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ from app import crud
|
||||
from app.core.config import settings
|
||||
from app.models import User, UserCreate
|
||||
|
||||
engine = create_engine(str(settings.SQLALCHEMY_DATABASE_URI))
|
||||
engine = create_engine(str(settings.DATABASE_URL))
|
||||
|
||||
|
||||
# make sure all SQLModel models are imported (app.models) before initializing DB
|
||||
|
||||
+3
-7
@@ -20,7 +20,7 @@ services:
|
||||
db:
|
||||
image: postgres:18
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres -d app"]
|
||||
interval: 10s
|
||||
retries: 5
|
||||
start_period: 30s
|
||||
@@ -29,8 +29,7 @@ services:
|
||||
- app-db-data:/var/lib/postgresql
|
||||
environment:
|
||||
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?Variable not set}
|
||||
- POSTGRES_USER=${POSTGRES_USER:?Variable not set}
|
||||
- POSTGRES_DB=${POSTGRES_DB:?Variable not set}
|
||||
- POSTGRES_DB=app
|
||||
|
||||
adminer:
|
||||
image: adminer
|
||||
@@ -62,10 +61,7 @@ services:
|
||||
SMTP_USER: ${SMTP_USER:-}
|
||||
SMTP_PASSWORD: ${SMTP_PASSWORD:-}
|
||||
EMAILS_FROM_EMAIL: ${EMAILS_FROM_EMAIL}
|
||||
POSTGRES_SERVER: db
|
||||
POSTGRES_DB: ${POSTGRES_DB:?Variable not set}
|
||||
POSTGRES_USER: ${POSTGRES_USER:?Variable not set}
|
||||
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?Variable not set}
|
||||
DATABASE_URL: postgresql://postgres:${POSTGRES_PASSWORD:?Variable not set}@db:5432/app
|
||||
SENTRY_DSN: ${SENTRY_DSN:-}
|
||||
|
||||
healthcheck:
|
||||
|
||||
+12
-50
@@ -4,13 +4,13 @@ You can deploy the project using Docker Compose to a remote server.
|
||||
|
||||
The deployment Docker Compose configuration includes Traefik to handle HTTPS and route incoming traffic to the application.
|
||||
|
||||
You can use CI/CD (continuous integration and continuous deployment) systems to deploy automatically, there are already configurations to do it with GitHub Actions.
|
||||
The included GitHub Actions workflow can deploy the application automatically.
|
||||
|
||||
But you have to configure a couple things first. 🤓
|
||||
|
||||
## Preparation
|
||||
|
||||
* Have a remote server ready and available. Use a separate server for each environment, for example one for staging and one for production.
|
||||
* Have a remote server ready and available.
|
||||
* Configure DNS records pointing to the server for the application domain and any supporting service subdomains you want to expose, e.g. `fastapi-project.example.com` and `adminer.fastapi-project.example.com`.
|
||||
* Install and configure [Docker](https://docs.docker.com/engine/install/) on the remote server (Docker Engine, not Docker Desktop).
|
||||
|
||||
@@ -80,16 +80,17 @@ You can set several other environment variables:
|
||||
* `SMTP_USER`: The SMTP server user to send emails.
|
||||
* `SMTP_PASSWORD`: The SMTP server password to send emails.
|
||||
* `EMAILS_FROM_EMAIL`: The email account to send emails from.
|
||||
* `POSTGRES_USER`: The Postgres user, you can leave the default.
|
||||
* `POSTGRES_DB`: The database name to use for this application. You can leave the default of `app`.
|
||||
* `SENTRY_DSN`: The DSN for Sentry, if you are using it.
|
||||
|
||||
## GitHub Actions Environment Variables
|
||||
## GitHub Repository Automation
|
||||
|
||||
There are some environment variables only used by GitHub Actions that you can configure:
|
||||
Install the following GitHub Apps to enable the included repository automation:
|
||||
|
||||
* `LATEST_CHANGES`: Used by the GitHub Action [latest-changes](https://github.com/tiangolo/latest-changes) to automatically add release notes based on the PRs merged. It's a personal access token, read the docs for details.
|
||||
* `SMOKESHOW_AUTH_KEY`: Used to handle and publish the code coverage using [Smokeshow](https://github.com/samuelcolvin/smokeshow), follow their instructions to create a (free) Smokeshow key.
|
||||
* [Latest Changes](https://github.com/apps/latest-changes) updates `release-notes.md` when a pull request is merged.
|
||||
* [PR Push](https://github.com/apps/pr-push) lets the pre-commit workflow push automated fixes to pull request branches.
|
||||
* [PR Submit](https://github.com/apps/pr-submit) lets the **Bump pre-commit hooks** and **Prepare Release** workflows create pull requests.
|
||||
|
||||
To publish code coverage with [Smokeshow](https://github.com/samuelcolvin/smokeshow), add `SMOKESHOW_AUTH_KEY` as a repository secret.
|
||||
|
||||
### Deploy with Docker Compose
|
||||
|
||||
@@ -108,7 +109,7 @@ The `compose.deploy.yml` file adds the deployment settings to the shared configu
|
||||
|
||||
You can use GitHub Actions to deploy your project automatically. 😎
|
||||
|
||||
There are already two environment deployments configured, `staging` and `production`. Each environment should be deployed to a separate server. 🚀
|
||||
The included `deploy.yml` workflow deploys the application whenever changes are pushed to `master`, including when a pull request is merged. 🚀
|
||||
|
||||
### Install GitHub Actions Runner
|
||||
|
||||
@@ -138,8 +139,6 @@ cd
|
||||
|
||||
* [Install a GitHub Action self-hosted runner following the official guide](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/adding-self-hosted-runners#adding-a-self-hosted-runner-to-a-repository).
|
||||
|
||||
* When asked about labels, add a label for the environment, e.g. `production`. You can also add labels later.
|
||||
|
||||
After installing, the guide would tell you to run a command to start the runner. Nevertheless, it would stop once you terminate that process or if your local connection to your server is lost.
|
||||
|
||||
To make sure it runs on startup and continues running, you can install it as a service. To do that, exit the `github` user and go back to the `root` user:
|
||||
@@ -182,17 +181,9 @@ cd /home/github/actions-runner
|
||||
|
||||
You can read more about it in the official guide: [Configuring the self-hosted runner application as a service](https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/configuring-the-self-hosted-runner-application-as-a-service).
|
||||
|
||||
### Configure GitHub Environments
|
||||
### Set Repository Secrets
|
||||
|
||||
The deployment workflows use [GitHub Environments](https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/manage-environments) for `staging` and `production`. This enables environment-specific secrets, deployment protection rules (e.g. required reviewers, wait timers), and deployment status tracking.
|
||||
|
||||
To configure them, go to your repository's **Settings** > **Environments** and create the `staging` and `production` environments.
|
||||
|
||||
### Set Secrets
|
||||
|
||||
For each GitHub Environment (`staging` and `production`), configure the required secrets as [environment secrets](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets#creating-secrets-for-an-environment). Environment secrets are preferred over [repository secrets](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets#creating-secrets-for-a-repository) because they are scoped to the specific environment, reducing exposure and aligning with any protection rules you configure.
|
||||
|
||||
The deployment workflows require these secrets:
|
||||
In your repository, go to **Settings** > **Secrets and variables** > **Actions** and add the following [repository secrets](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets#creating-secrets-for-a-repository):
|
||||
|
||||
* `DOMAIN`
|
||||
* `FIRST_SUPERUSER`
|
||||
@@ -209,41 +200,12 @@ To enable emails, configure these additional secrets with the values from your e
|
||||
|
||||
To enable Sentry, configure the `SENTRY_DSN` secret.
|
||||
|
||||
## GitHub Action Deployment Workflows
|
||||
|
||||
There are GitHub Action workflows in the `.github/workflows` directory already configured for deploying to the environments (GitHub Actions runners with the labels):
|
||||
|
||||
* `staging`: after pushing (or merging) to the branch `master`.
|
||||
* `production`: after publishing a release.
|
||||
|
||||
Both workflows are associated with their respective GitHub Environments, so deployments will be visible in the repository's **Environments** section and will respect any protection rules you configure.
|
||||
|
||||
### Prepare a Release
|
||||
|
||||
Install the [PR Submit GitHub App](https://github.com/apps/pr-submit) in your repository to enable the **Prepare Release** workflow.
|
||||
|
||||
Run the workflow manually from the **Actions** tab and select the version bump. It creates a pull request that updates the release notes. When you merge that pull request, the **Create Draft Release** workflow creates a draft GitHub release with the corresponding release notes.
|
||||
|
||||
Review and publish the draft release to trigger the production deployment.
|
||||
|
||||
If you need to add extra environments you could use those as a starting point.
|
||||
|
||||
## URLs
|
||||
|
||||
Replace `fastapi-project.example.com` with your domain.
|
||||
|
||||
### Production
|
||||
|
||||
Application (frontend and API): `https://fastapi-project.example.com`
|
||||
|
||||
Interactive API docs: `https://fastapi-project.example.com/docs`
|
||||
|
||||
Adminer: `https://adminer.fastapi-project.example.com`
|
||||
|
||||
### Staging
|
||||
|
||||
Application (frontend and API): `https://staging.fastapi-project.example.com`
|
||||
|
||||
Interactive API docs: `https://staging.fastapi-project.example.com/docs`
|
||||
|
||||
Adminer: `https://adminer.staging.fastapi-project.example.com`
|
||||
|
||||
+1
-1
@@ -156,7 +156,7 @@ biome check..............................................................Passed
|
||||
|
||||
## URLs
|
||||
|
||||
The production or staging URLs would use these same paths, but with your own domain.
|
||||
The deployed URLs use these same paths, but with your own domain.
|
||||
|
||||
### Development URLs
|
||||
|
||||
|
||||
@@ -2,6 +2,17 @@
|
||||
|
||||
## Latest Changes
|
||||
|
||||
## 0.11.1 (2026-08-11)
|
||||
|
||||
### Refactors
|
||||
|
||||
* ♻️ Simplify Docker Compose deployment. PR [#2435](https://github.com/fastapi/full-stack-fastapi-template/pull/2435) by [@tiangolo](https://github.com/tiangolo).
|
||||
* ♻️ Migrate to `DATABASE_URL` instead of separate variables. PR [#2184](https://github.com/fastapi/full-stack-fastapi-template/pull/2184) by [@patrick91](https://github.com/patrick91).
|
||||
|
||||
### Internal
|
||||
|
||||
* ⬆ Bump the npm-packages group across 1 directory with 33 updates. PR [#2433](https://github.com/fastapi/full-stack-fastapi-template/pull/2433) by [@dependabot[bot]](https://github.com/apps/dependabot).
|
||||
|
||||
## 0.11.0 (2026-08-11)
|
||||
|
||||
### Features
|
||||
|
||||
Reference in New Issue
Block a user