Deploy a FastAPI app to a VPS with Pystrano

A practical guide for a FastAPI app running behind Uvicorn and systemd on a VPS-style Linux server.

Pystrano helps automate deployment steps. It does not replace server hardening, backups, monitoring, TLS setup, database administration, or full infrastructure provisioning.

What this guide covers

This guide covers a FastAPI deployment flow where Pystrano clones your Git repository into a timestamped release directory, installs Python dependencies, links shared files, optionally runs Alembic migrations and static asset commands, updates the current symlink, restarts a systemd service, and prunes old releases.

Server assumptions

  • You have a VPS-style Linux server where you can SSH as root for setup.
  • The server can reach your Git repository over SSH.
  • System packages can be installed with apt.
  • Uvicorn is managed by a systemd service file that you provide or generate with pystrano init.
  • Database, TLS, firewall, backups, and monitoring are handled outside Pystrano.

FastAPI project assumptions

  • Your repository contains a dependency file such as uv.lock or requirements.txt.
  • Your FastAPI app can be served by Uvicorn from the module path used in your systemd service.
  • Your production settings read environment variables from the environment Pystrano loads from your dotenv file.
  • If you enable migrations, Alembic is available or migration_command points to the command your app uses.

Example deployment config

config_version: 2

common:
  source_code_url: "git@github.com:example/example-fastapi-app.git"
  framework: "fastapi"
  project_root: "apps/example-fastapi-app"
  project_user: "deploy"
  venv_dir: ".venv"
  package_manager: "uv"
  dependency_file: "uv.lock"
  keep_releases: 5
  system_packages: |
    libpq-dev
    python3-dev
    build-essential
  env_file: "./deploy/example_fastapi_app/production/.env"
  ssh_known_hosts: "github.com"
  service_file: "./deploy/example_fastapi_app/production/uvicorn.service"
  secrets: "./deploy/example_fastapi_app/production/app-secret.txt"
  branch: "main"
  clone_depth: 1
  migration_command: "/home/deploy/.venv/bin/alembic upgrade head"

servers:
  - host: "app1.example.com"
    port: 22
    run_migrations: true
    collect_static_files: false

Uvicorn and systemd notes

When service_file is configured, setup copies it to /etc/systemd/system/<service_file_name>, reloads systemd, and enables the service. Deploy restarts that service after promoting the new release.

pystrano init can generate a starter uvicorn.service. Review the module path, environment variables, worker settings, and socket or port binding before using it for production traffic.

Dependency installation

The example uses package_manager: uv with dependency_file: uv.lock. Pystrano ensures uv exists in the virtualenv, then runs a frozen uv sync against that virtualenv. FastAPI apps can also use package_manager: pip with requirements.txt, or dependency_install_command for a custom install flow.

Running migrations

Set run_migrations: true on the server that should run FastAPI migrations. By default Pystrano runs:

/home/deploy/.venv/bin/alembic upgrade head

Set migration_command when your app uses a different Alembic path, Python module invocation, or migration system.

Static asset commands

FastAPI does not have a built-in static collection command. If a server has collect_static_files: true, set static_files_command to the exact command Pystrano should run during deploy.

Deploying

Preview first:

pystrano deploy production example_fastapi_app --dry-run

Deploy when ready:

pystrano deploy production example_fastapi_app

Rolling back

Pystrano keeps timestamped release directories and promotes a release by updating the current symlink. It does not currently include a rollback CLI command. Keep keep_releases high enough for your recovery window and document a tested manual rollback procedure for your servers.

Troubleshooting

  • Use --dry-run before live changes to inspect remote commands.
  • Use --verbose when Fabric, Invoke, or Paramiko output is needed.
  • Confirm the server can clone the repository and reach the configured Git host.
  • Confirm uv, Alembic, and any custom static build commands are available after dependency installation.
  • Confirm local paths referenced by env_file, service_file, and secrets exist before running setup or deploy.