A template repository for deploying a FastAPI application with PostgreSQL, JWT authentication, and Alembic migrations using Coolify.
- FastAPI with Python 3.12, PostgreSQL 17
- JWT authentication (register, login, protected routes)
- SQLAlchemy 2.0 ORM with Alembic migrations
- Docker Compose with 2 services:
web,postgres - GitHub Actions CI with linting (Ruff), security scanning (Bandit, pip-audit), and tests (pytest)
- Dependabot for automated dependency updates
- Health check endpoint at
/up - Interactive API docs at
/docs(Swagger UI)
- A running Coolify instance (self-hosted or cloud)
- A GitHub (or GitLab/Bitbucket) account
- A server connected to Coolify
Click "Use this template" on GitHub to create your own repository, or fork/clone it.
If your repository is private, you need to grant Coolify access. There are two options:
- Go to Coolify Dashboard → Sources → "+ Add".
- Select "GitHub App".
- Enter a name for the integration (e.g.
my-coolify). - Click "Register on GitHub" — this redirects you to GitHub.
- On GitHub, choose where to install the app:
- Select your user account or organization.
- Choose "Only select repositories" and pick the repo(s) you want to deploy.
- Click "Install & Authorize".
- You'll be redirected back to Coolify. The GitHub App is now connected.
- Go to Coolify Dashboard → Security → Private Keys.
- Click "+ Add" to create a new SSH key.
- Copy the public key and add it to your GitHub repository → Settings → Deploy keys.
- Log in to your Coolify dashboard.
- Navigate to your Project (or create a new one).
- Click "+ New" to add a new resource.
- Choose based on your setup:
- GitHub App: Select "Private Repository (GitHub App)" → pick the GitHub App → select your repository.
- Deploy Key: Select "Private Repository (Deploy Key)" → select the key → enter the repository URL.
- Public repo: Select "Public Repository" → enter the repository URL.
- Build Pack: Select "Docker Compose".
- Docker Compose File: Set to
docker-compose.coolify.yaml. - Branch: Select the branch to deploy (usually
main).
Go to the resource's "Environment Variables" section and add the following:
| Variable | Required | Description |
|---|---|---|
SECRET_KEY |
Yes | Random secret for JWT tokens (openssl rand -hex 32) |
POSTGRES_PASSWORD |
Yes | PostgreSQL password (choose a strong one) |
POSTGRES_DB |
No | PostgreSQL database name (default: coolify_fastapi_production) |
SMTP_ADDRESS |
No | SMTP server address (optional, for email features) |
SMTP_PORT |
No | SMTP port (default: 587) |
SMTP_USERNAME |
No | SMTP username |
SMTP_PASSWORD |
No | SMTP password |
SMTP_FROM |
No | From address for emails (default: noreply@example.com) |
Tip: Generate a secure secret key with:
openssl rand -hex 32
- In the resource settings, find the
webservice and go to its "Domains" or network settings. - Add your custom domain (e.g.
https://api.example.com) or use the auto-generated Coolify URL. - Coolify will automatically provision an SSL certificate via Let's Encrypt.
- Make sure the port mapping points to port 8000.
Click "Deploy" to trigger the first deployment. Coolify will:
- Build the Docker image (multi-stage, slim Python image).
- Start PostgreSQL and wait for it to be healthy.
- Run Alembic migrations automatically.
- Start the FastAPI app via Uvicorn on port 8000.
Webhooks are configured automatically. Every push to your configured branch triggers a new deployment.
-
Generate a webhook secret:
openssl rand -hex 32 -
Copy the Webhook URL from your resource in Coolify → "Webhooks".
-
Add the webhook in GitHub: Settings → Webhooks → "Add webhook".
Field Value Payload URL The Webhook URL from Coolify Content type application/jsonSecret The secret you generated with opensslEvents Select "Just the push event"
┌──────────────────────────────────────┐
│ Docker Compose │
│ │
│ ┌──────────────┐ │
│ │ web │ │
│ │ FastAPI + │ │
│ │ Uvicorn │ │
│ │ :8000 │ │
│ └──────┬───────┘ │
│ │ │
│ ┌──────┴───────┐ │
│ │ PostgreSQL │ │
│ │ :5432 │ │
│ └──────────────┘ │
└──────────────────────────────────────┘
| Service | Description |
|---|---|
| web | FastAPI app with Uvicorn (port 8000). Serves API + interactive docs. |
| postgres | PostgreSQL 17 Alpine. Persistent data volume. |
| Method | Path | Description | Auth |
|---|---|---|---|
| GET | /up |
Health check | No |
| GET | /docs |
Swagger UI | No |
| POST | /api/auth/register |
Register a new user | No |
| POST | /api/auth/login |
Login, get JWT token | No |
| GET | /api/users/me |
Get current user | Bearer |
# Create virtual environment
python -m venv .venv
source .venv/bin/activate # or: .venv/bin/python for fish shell
# Install dependencies
pip install -e ".[dev]"
# Copy environment file
cp .env.example .env
# Start PostgreSQL (e.g. via Docker)
docker run -d --name pg -p 5432:5432 -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=fastapi_dev postgres:17-alpine
# Run migrations
alembic upgrade head
# Start the server
uvicorn app.main:app --reloadThe API will be available at http://localhost:8000.
Interactive docs at http://localhost:8000/docs.
.
├── docker-compose.coolify.yaml # Coolify deployment config
├── Dockerfile # Multi-stage production build
├── pyproject.toml # Python dependencies & tool config
├── alembic.ini # Alembic configuration
├── alembic/
│ ├── env.py # Migration environment
│ └── versions/ # Migration files
├── app/
│ ├── main.py # FastAPI application entry
│ ├── config.py # Settings (pydantic-settings)
│ ├── database.py # SQLAlchemy engine & session
│ ├── core/ # Security (JWT, password hashing)
│ ├── models/ # SQLAlchemy models
│ ├── schemas/ # Pydantic schemas
│ └── api/
│ ├── health.py # Health check endpoint
│ ├── auth.py # Register & login
│ ├── users.py # User endpoints
│ └── deps.py # Dependencies (auth, DB)
├── tests/ # pytest test suite
├── bin/
│ └── docker-entrypoint.sh # Container startup script
└── .github/
├── workflows/ci.yml # CI pipeline
└── dependabot.yml # Dependency updates
| Problem | Solution |
|---|---|
| Database connection refused | Check that PostgreSQL is healthy and POSTGRES_PASSWORD is set |
SECRET_KEY error |
Ensure the env var is set — generate with openssl rand -hex 32 |
| Migration errors | Check Alembic logs: docker compose logs web |
| 401 on protected endpoints | Include Authorization: Bearer <token> header from login response |
| Deployment hangs | The web service waits for PostgreSQL healthcheck — check postgres logs |