A newer version of the Gradio SDK is available: 6.20.0
CliniqAI β Setup & Run Instructions
Complete guide for Windows (WSL2) and macOS (Intel + Apple Silicon).
Table of Contents
- How the application runs
- Prerequisites by operating system
- Get the code
- Install Python tooling with uv
- Configure environment variables
- Start infrastructure (Docker)
- Apply the database schema
- Run the application
- Verify everything is working
- Stopping the application
- Hugging Face Spaces deployment
- Troubleshooting by OS
1. How the application runs
CliniqAI consists of several cooperating processes. Understanding what each one does prevents confusion when something stops working.
| Process | Command | Port | What it does | Required for |
|---|---|---|---|---|
| PostgreSQL | make up |
5432 | Stores appointments, staff, SOAP drafts, audit log | Everything |
| Redis | make up |
6379 | Celery task broker + result backend | Scheduled reminders |
| Langfuse | make up |
3001 | LLM call observability dashboard | Observability (optional) |
| FastAPI backend | make api |
8000 | All agent HTTP endpoints | Everything |
| Gradio UI | make ui |
7860 | Doctor / Patient / Nurse tabs | The web interface |
| Celery worker | make worker |
β | Executes scheduled reminder tasks | T-24h / T-3h / T-2h reminders |
| Celery beat | make beat |
β | Triggers the 6-hour sweep cron | Post-discharge sweep |
Minimum to run the demo: Docker (PostgreSQL + Redis), FastAPI, and Gradio. The Celery worker and beat are only needed for timed notifications and scheduled sweeps.
2. Prerequisites by operating system
2.1 Windows β install WSL2 first
Native Windows is not supported. All development happens inside WSL2 (Windows Subsystem for Linux). Docker Desktop runs on Windows but mounts into WSL2.
Step 1 β Enable WSL2
Open PowerShell as Administrator and run:
wsl --install
This installs WSL2 and Ubuntu by default. If you already have WSL1, upgrade:
wsl --set-default-version 2
wsl --install -d Ubuntu-24.04
Restart your machine when prompted.
Step 2 β Verify WSL2 is running
wsl --list --verbose
The VERSION column must show 2. If it shows 1, run:
wsl --set-version Ubuntu-24.04 2
Step 3 β Install Docker Desktop for Windows
Download from: https://www.docker.com/products/docker-desktop
During installation, on the Configuration screen, make sure "Use WSL 2 instead of Hyper-V" is checked.
After installation:
- Open Docker Desktop β Settings β Resources β WSL Integration
- Enable integration for your Ubuntu distribution
- Click Apply & Restart
Verify inside WSL:
# Open Ubuntu from the Start menu, then:
docker --version
docker compose version
Both commands must work inside the WSL terminal. If docker is not found inside WSL, go back to Docker Desktop β WSL Integration and re-enable your distro.
Step 4 β Install build tools inside WSL
sudo apt update && sudo apt upgrade -y
sudo apt install -y build-essential git curl wget make python3-dev
Step 5 β Verify Make is available
make --version
If missing: sudo apt install -y make
2.2 macOS
macOS works natively β no virtualization layer needed.
Step 1 β Install Xcode Command Line Tools
This provides git, make, and the C compiler:
xcode-select --install
Click Install in the dialog that appears. This takes a few minutes.
Step 2 β Install Homebrew
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
After installation, follow the instructions in the terminal to add Homebrew to your PATH. For Apple Silicon Macs, this is usually:
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> ~/.zprofile
eval "$(/opt/homebrew/bin/brew shellenv)"
For Intel Macs, Homebrew installs to /usr/local which is already on PATH.
Verify:
brew --version
Step 3 β Install Docker Desktop for macOS
Download the correct version for your chip:
- Apple Silicon (M1/M2/M3/M4): https://docs.docker.com/desktop/install/mac-install/ β choose Apple Chip
- Intel: same page β choose Intel Chip
After installation, open Docker Desktop from Applications and wait for the whale icon in the menu bar to stop animating (means Docker engine is ready).
Verify:
docker --version
docker compose version
3. Get the code
Windows users: Clone inside WSL, not on the Windows filesystem (/mnt/c/...). The Windows filesystem is mounted via 9P protocol and causes dramatic slowdowns with Python tooling.
# Good β inside WSL home directory
cd ~
git clone https://github.com/your-org/clinical-pilot-hf.git
cd clinical-pilot-hf
# Bad β this is slow
# cd /mnt/c/Users/yourname/projects
macOS users: Clone anywhere in your home directory.
cd ~/projects # or wherever you keep code
git clone https://github.com/your-org/clinical-pilot-hf.git
cd clinical-pilot-hf
4. Install Python tooling with uv
uv is the package manager used by this project. It is faster than pip and manages Python versions automatically.
Install uv
WSL (Ubuntu) and macOS:
curl -LsSf https://astral.sh/uv/install.sh | sh
After installation, reload your shell:
source ~/.bashrc # WSL / bash users
source ~/.zshrc # macOS zsh users (default since macOS Catalina)
Verify:
uv --version
Install Python 3.12 and all project dependencies
From the project root directory:
make install
This runs uv sync under the hood, which:
- Downloads Python 3.12 if not present on your machine
- Creates a virtual environment at
.venv/ - Installs all packages from
pyproject.toml
Note on
torchandtransformers: These are large packages (torch alone is ~2 GB). The firstmake installwill take several minutes while they download. Subsequent installs are cached.
Apple Silicon note:
torchships with Metal (MPS) support on Apple Silicon. No special flags needed β the package auto-detects the chip.
Verify the virtual environment was created:
ls .venv/
You should see bin/, lib/, and pyvenv.cfg.
5. Configure environment variables
Copy the template to a local file that is gitignored:
cp .env.example .env.local
Now edit .env.local. The sections below explain every variable.
# WSL / macOS β use any editor
nano .env.local
# or
code .env.local # VS Code (works in both WSL and macOS)
5.1 Required β LLM provider
The application needs at least one LLM provider key to run real agent calls. Without any key the app falls back to mock responses, which is fine for UI testing but means no real SOAP notes, routing, or appointment confirmation messages will be generated.
Option A β Groq (recommended, free, no credit card)
- Go to https://console.groq.com
- Sign up or log in
- Click API Keys in the left sidebar
- Click Create API Key
- Copy the key (it starts with
gsk_)
Set in .env.local:
GROQ_API_KEY=gsk_your_key_here
Groq gives you free access to Qwen3-32B (used for SOAP notes and handovers) and Llama 3.1 8B (used for routing and reminders). No billing required.
Option B β OpenRouter (free tier fallback)
- Go to https://openrouter.ai
- Create an account
- Go to Keys β Create Key
- Copy the key (it starts with
sk-or-)
Set in .env.local:
OPENROUTER_API_KEY=sk-or-your_key_here
Free models on OpenRouter have a limit of 20 requests per minute and 200 per day. Use this as a backup when Groq rate limits.
Option C β Ollama (offline, no API key needed)
For fully offline operation, install Ollama:
macOS:
brew install ollama
ollama serve & # starts the Ollama daemon in background
ollama pull qwen2.5:32b # downloads the model (~20 GB β do this once)
WSL:
curl -fsSL https://ollama.com/install.sh | sh
ollama serve &
ollama pull qwen2.5:32b
Set in .env.local:
OLLAMA_BASE_URL=http://localhost:11434
Disk requirement: Qwen2.5 32B requires approximately 20 GB of free disk space. The model downloads once and is cached at
~/.ollama/models/.
5.2 Optional integrations
These are all optional. The application runs and demos without any of them.
Telegram notifications
Required if you want instant push notifications when appointments are booked or cancelled.
- Open Telegram and search for
@BotFather - Send
/newbot - Follow the prompts to choose a name and username for your bot
- Copy the HTTP API token BotFather gives you (format:
1234567890:ABCdefGhIJKlmNoPQRsTUVwxyZ) - Add your bot to a group or channel, or start a direct chat with it and send a message
- Fetch
https://api.telegram.org/bot<YOUR_TOKEN>/getUpdatesin a browser - Copy the
chat.idvalue from the response (negative number for groups, positive for DMs)
TELEGRAM_BOT_TOKEN=1234567890:ABCdefGhIJKlmNoPQRsTUVwxyZ
TELEGRAM_CHAT_ID=-1001234567890
Langfuse observability (optional but useful)
Langfuse is already included in the Docker stack. It runs at http://localhost:3001.
- Open http://localhost:3001 in your browser (after
make up) - Create an account (local β not sent anywhere)
- Create a project called
cliniqai - Go to Settings β API Keys
- Copy the public key and secret key
LANGFUSE_HOST=http://localhost:3001
LANGFUSE_PUBLIC_KEY=pk-lf-your_public_key
LANGFUSE_SECRET_KEY=sk-lf-your_secret_key
WhatsApp via Twilio (optional β for production)
The demo uses Gradio's chat widget instead of WhatsApp. WhatsApp requires Meta approval which takes days, so it is a post-hackathon integration.
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_AUTH_TOKEN=your_auth_token
TWILIO_WHATSAPP_FROM=whatsapp:+14155238886
Google Calendar and Gmail MCP (optional)
Required only if you want real Google Calendar slot checking instead of the local Postgres calendar backend (which is the default). The local backend works for demos without any Google credentials.
The calendar backend is controlled by:
CALENDAR_BACKEND=local # default β uses Postgres, no Google needed
# CALENDAR_BACKEND=google # uncomment to use Google Calendar MCP
5.3 Complete .env.local reference
Below is a fully annotated .env.local with every field explained. Copy this and fill in the values you have.
# ============================================================
# DATABASE β do not change these for local dev
# They match what docker-compose.dev.yml creates
# ============================================================
POSTGRES_USER=cliniq
POSTGRES_PASSWORD=cliniq
POSTGRES_DB=cliniq
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
DATABASE_URL=postgresql+psycopg://cliniq:cliniq@localhost:5432/cliniq
# ============================================================
# REDIS β do not change for local dev
# ============================================================
REDIS_URL=redis://localhost:6379/0
CELERY_BROKER_URL=redis://localhost:6379/1
CELERY_RESULT_BACKEND=redis://localhost:6379/2
# ============================================================
# LLM PROVIDERS β set at least one
# ============================================================
GROQ_API_KEY= # https://console.groq.com β free, no card
OPENROUTER_API_KEY= # https://openrouter.ai β free fallback
OLLAMA_BASE_URL=http://localhost:11434 # local offline option
# ============================================================
# WHISPER TRANSCRIPTION
# groq β uses Groq API (fast, needs GROQ_API_KEY)
# local β uses local transformers pipeline (slow first run, offline)
# auto β tries groq, falls back to local if GROQ_API_KEY unset
# ============================================================
WHISPER_MODEL=openai/whisper-large-v3-turbo
WHISPER_BACKEND=groq
# ============================================================
# TELEGRAM (optional β for instant push notifications)
# ============================================================
TELEGRAM_BOT_TOKEN= # from @BotFather
TELEGRAM_CHAT_ID= # numeric id from getUpdates
# ============================================================
# CALENDAR BACKEND
# local β Postgres-backed mini calendar (default, no Google needed)
# google β Google Calendar MCP (requires OAuth token)
# ============================================================
CALENDAR_BACKEND=local
# ============================================================
# LANGFUSE OBSERVABILITY (optional)
# Runs locally at :3001 after `make up`
# ============================================================
LANGFUSE_HOST=http://localhost:3001
LANGFUSE_PUBLIC_KEY= # from http://localhost:3001 after first login
LANGFUSE_SECRET_KEY=
# ============================================================
# TWILIO WHATSAPP (optional β production only)
# Demo uses Gradio chat widget on the same code path
# ============================================================
TWILIO_ACCOUNT_SID=
TWILIO_AUTH_TOKEN=
TWILIO_WHATSAPP_FROM=whatsapp:+14155238886
# ============================================================
# GOOGLE MCP URLS β leave as-is unless changing backends
# ============================================================
GOOGLE_CALENDAR_MCP_URL=https://calendarmcp.googleapis.com
GMAIL_MCP_URL=https://gmailmcp.googleapis.com
GOOGLE_DRIVE_MCP_URL=https://drivemcp.googleapis.com
# ============================================================
# APP
# ============================================================
CLINIQ_ENV=dev
CLINIQ_API_KEY=dev-local-key
HF_SPACE_BACKEND_URL=http://localhost:8000
6. Start infrastructure (Docker)
Make sure Docker Desktop is running (the whale icon in the system tray / menu bar is active and not animating).
From the project root:
make up
This starts three containers:
cliniqai-postgresβ PostgreSQL 16 on port 5432cliniqai-redisβ Redis 7 on port 6379cliniqai-langfuseβ Langfuse on port 3001
Verify all three containers are healthy:
docker compose ps
Expected output:
NAME STATUS
cliniqai-postgres Up (healthy)
cliniqai-redis Up (healthy)
cliniqai-langfuse Up (healthy)
If any container shows unhealthy or Exiting, check logs:
docker compose logs postgres
docker compose logs redis
docker compose logs langfuse
7. Apply the database schema
This creates all tables (patients, appointments, staff, shifts, soap_drafts, audit_log, etc.):
make migrate
You should see Alembic output ending with:
INFO [alembic.runtime.migration] Running upgrade -> 0001_init, initial schema
If you see connection refused errors, Docker has not finished starting yet. Wait 10 seconds and retry.
Verify tables were created:
docker exec -it cliniqai-postgres psql -U cliniq -d cliniq -c "\dt"
Expected output lists: appointments, audit_log, discharges, messages, patients, shifts, soap_drafts, staff.
8. Run the application
Each process below needs its own terminal window. Open new terminals before running each command. In VS Code you can use the split terminal button (Ctrl+Shift+5 / Cmd+Shift+5) to open multiple panes.
8.1 Minimal mode (no Celery)
This is sufficient for the demo β UI works, agents work, mocks work. Timed reminders (T-3h, T-24h) will not fire, but instant Telegram notifications still work.
Terminal 1 β FastAPI backend:
make api
Wait until you see:
INFO: Application startup complete.
INFO: Uvicorn running on http://0.0.0.0:8000
Terminal 2 β Gradio UI:
make ui
Wait until you see:
Running on local URL: http://127.0.0.1:7860
Open http://localhost:7860 in your browser. You should see the CliniqAI tabs: Doctor, Patient, Nurse & Admin.
8.2 Full mode (all processes)
Open five separate terminals from the project root.
Terminal 1 β Infrastructure (if not already running):
make up
Terminal 2 β FastAPI backend:
make api
Terminal 3 β Gradio UI:
make ui
Terminal 4 β Celery worker:
make worker
Wait until you see:
[tasks] Registered: appointment.send_reminder
[tasks] Registered: appointment.check_no_show
celery@hostname ready.
Terminal 5 β Celery beat (cron scheduler):
make beat
Wait until you see:
beat: Starting...
Scheduler: PersistentScheduler
Terminal layout reference
βββββββββββββββββββββββ¬ββββββββββββββββββββββ
β Terminal 1 β Terminal 2 β
β make up β make api β
β (infrastructure) β (FastAPI :8000) β
βββββββββββββββββββββββΌββββββββββββββββββββββ€
β Terminal 3 β Terminal 4 β
β make ui β make worker β
β (Gradio :7860) β (Celery worker) β
βββββββββββββββββββββββ΄ββββββββββββββββββββββ€
β Terminal 5 β
β make beat (Celery beat / cron) β
βββββββββββββββββββββββββββββββββββββββββββββ
9. Verify everything is working
Run these checks after starting all processes. Each check is independent β run them in any free terminal.
Check 1 β Health endpoint
curl http://localhost:8000/health
Expected:
{"status": "ok", "env": "dev", "service": "cliniqai-backend"}
Check 2 β Orchestrator routing
curl -s -X POST http://localhost:8000/orchestrate \
-H "content-type: application/json" \
-d '{"utterance": "book me an appointment"}' | python3 -m json.tool
Expected: a JSON object with current_agent: "appointment" and a communication field containing an appointment reply draft.
Check 3 β SOAP note mock
curl -s -X POST http://localhost:8000/documentation/draft \
-H "content-type: application/json" \
-d '{"patient_id": "p-001"}' | python3 -m json.tool
Expected: a JSON object with draft_id, soap (subjective/objective/assessment/plan), icd10_suggestions, and sources.
Check 4 β Roster generation (mock data)
curl -s -X POST http://localhost:8000/roster/generate \
-H "content-type: application/json" \
-d '{}' | python3 -m json.tool
Expected: a JSON object with roster_id, assignments (array of shift objects), and fairness_score.
Check 5 β Handover brief
curl -s -X POST http://localhost:8000/handover/generate \
-H "content-type: application/json" \
-d '{"ward_id": "ward-A"}' | python3 -m json.tool
Expected: a JSON object with brief_id, patients array ordered urgent β watch β stable.
Check 6 β Database connectivity
curl -s -X POST http://localhost:8000/appointment/book \
-H "content-type: application/json" \
-d '{"patient_id": "p-test-001"}' | python3 -m json.tool
Expected: a BookingResult with appointment_id, slot_iso, and status: "confirmed". If you see connection refused or a 500 error mentioning PostgreSQL, the database container is not running.
Check 7 β Full test suite
make test
Expected: all tests pass. Contract tests and routing smoke tests run without any external keys.
Check 8 β Linter
make lint
Expected: no output (clean). Warnings are acceptable, errors mean something changed in contracts.
Check 9 β OpenAPI docs
Open http://localhost:8000/docs in your browser. You should see the Swagger UI with all agent endpoints grouped by tag: documentation, appointment, clerical, discharge, roster, handover.
Check 10 β Telegram (if configured)
python3 -c "
from dotenv import load_dotenv
load_dotenv('.env.local')
from app.integrations.telegram import send
result = send('CliniqAI test message β setup complete')
print(result)
"
Expected: {'status': 'sent', 'chat_id': '-100...', 'message_id': 123} and a message appearing in your Telegram.
If you see {'status': 'stub', ...}, the token or chat ID is not set in .env.local. See section 5.2.
10. Stopping the application
Stop individual processes
Press Ctrl+C in each terminal. Gradio, FastAPI, Celery worker, and beat all respond to Ctrl+C.
Stop Docker containers
make down
This stops and removes the containers but preserves data. PostgreSQL data persists in a Docker volume named cliniqai_postgres_data. Your appointments, staff records, and SOAP drafts survive a make down.
Stop Docker containers and wipe all data
Use this when you want a completely clean slate (e.g., schema changes require recreating the database):
docker compose down -v
The -v flag removes the volumes. Run make migrate again after this to recreate the schema.
11. Hugging Face Spaces deployment
The Gradio UI is designed to deploy to Hugging Face Spaces. The FastAPI backend must be deployed separately (e.g., on a cloud VM) because Spaces runs only the Gradio process.
Step 1 β Push to Hugging Face Spaces
- Create a Space at https://huggingface.co/spaces
- Choose Gradio as the SDK
- Choose Python 3.12 as the runtime
- Set the Space as private (HIPAA considerations)
# Add the HF Space as a remote
git remote add space https://huggingface.co/spaces/your-username/cliniqai
# Push the deploy folder
git subtree push --prefix deploy/hf_space space main
Step 2 β Configure Secrets in the Space
In your Space β Settings β Repository secrets, add:
| Secret | Value |
|---|---|
HF_SPACE_BACKEND_URL |
https://your-backend-api.example.com |
GROQ_API_KEY |
Your Groq key |
TELEGRAM_BOT_TOKEN |
Your bot token |
TELEGRAM_CHAT_ID |
Your chat ID |
The Space reads secrets as environment variables at runtime. Never commit secrets to git.
Step 3 β Verify Space deployment
After the Space builds (watch the build logs in the Spaces UI), open the Space URL and confirm all three tabs load and the backend URL shown at the top matches your deployed API.
12. Troubleshooting by OS
Windows / WSL
Problem: make: command not found inside WSL
sudo apt install -y make
Problem: docker: command not found inside WSL even though Docker Desktop is installed
Open Docker Desktop β Settings β Resources β WSL Integration. Toggle off, save, toggle on, save again. Then restart the WSL terminal:
wsl --shutdown # run this in PowerShell
# Then reopen Ubuntu
Problem: curl to localhost:8000 times out from inside WSL
FastAPI binds to 0.0.0.0:8000 which is accessible from inside WSL as localhost. If it still times out, check that FastAPI started without errors and that Windows Firewall is not blocking port 8000. Try from a Windows browser using the IP shown in ip addr show eth0 inside WSL.
Problem: Files cloned on /mnt/c/ cause very slow uv sync
Move the project into the WSL filesystem:
cp -r /mnt/c/Users/yourname/clinical-pilot-hf ~/clinical-pilot-hf
cd ~/clinical-pilot-hf
Problem: psycopg fails with library not found for -lssl
sudo apt install -y libpq-dev libssl-dev
make install # reinstall dependencies
Problem: torch install fails or takes forever in WSL
torch is large (~2 GB download). If it hangs, check your WSL memory limit. Create %USERPROFILE%\.wslconfig on Windows with:
[wsl2]
memory=8GB
processors=4
Then restart WSL: wsl --shutdown in PowerShell.
macOS
Problem: make install fails with error: externally-managed-environment
This means system Python is being used instead of uv's managed Python. Make sure you installed uv and that ~/.local/bin (or ~/.cargo/bin) is on your PATH:
echo $PATH # should include ~/.local/bin
source ~/.zshrc
uv --version
Problem: psycopg[binary] install fails on Apple Silicon
The binary wheel for psycopg may not build without libpq. Install it via Homebrew:
brew install libpq
export LDFLAGS="-L/opt/homebrew/opt/libpq/lib"
export CPPFLAGS="-I/opt/homebrew/opt/libpq/include"
make install
Problem: torch install is very slow on Apple Silicon
This is expected on first install β PyTorch for Apple Silicon is about 700 MB. Let it complete. Subsequent installs use the uv cache and are instant.
Problem: Docker Desktop is not starting on macOS
- Open Activity Monitor and search for
Docker - If it is running but unresponsive, force quit it
- Restart Docker Desktop from Applications
- If it still fails, reset to factory defaults: Docker menu β Troubleshoot β Reset to factory defaults
Problem: Port 5432 already in use
You have a local PostgreSQL installation running alongside the Docker one. Stop it:
# macOS native Postgres (if installed via Homebrew)
brew services stop postgresql@16
# or
pg_ctl stop -D /opt/homebrew/var/postgresql@16
Then re-run make up.
Problem: Port 6379 already in use
You have a local Redis running:
brew services stop redis
Then re-run make up.
Both platforms
Problem: RuntimeError: router: all models in chain for task=... failed
No LLM provider key is configured or all are rate-limited. Check:
python3 -c "
import os
from dotenv import load_dotenv
load_dotenv('.env.local')
print('GROQ:', bool(os.getenv('GROQ_API_KEY')))
print('OPENROUTER:', bool(os.getenv('OPENROUTER_API_KEY')))
print('OLLAMA:', bool(os.getenv('OLLAMA_BASE_URL')))
"
At least one must print True. If all print False, your .env.local is not being found or the keys are empty.
Problem: connection refused on port 5432
PostgreSQL container is not running. Check:
docker compose ps
docker compose logs postgres
Common causes: Docker Desktop is not running, or a previous docker compose down -v wiped the data and make migrate has not been re-run.
Problem: Alembic migration fails with relation already exists
The schema was partially applied. Run a full reset:
docker compose down -v
make up
# wait 10 seconds for postgres to become ready
make migrate
Problem: ModuleNotFoundError: No module named 'app'
The virtual environment is not activated or the package is not installed in editable mode. Run:
make install # re-installs everything
Or manually activate:
source .venv/bin/activate
Problem: Gradio UI shows ConnectionError β backend unreachable
The FastAPI backend is not running. Make sure make api is running in a separate terminal and http://localhost:8000/health returns {"status":"ok"}.
Problem: Telegram stub mode β notifications not arriving
Either TELEGRAM_BOT_TOKEN or TELEGRAM_CHAT_ID is empty in .env.local. Both are required. See section 5.2 for how to obtain them. After setting them, restart FastAPI and the Celery worker.
Problem: Celery tasks are queued but never execute
The Celery worker is not running. Open a new terminal and run make worker. You should see tasks being picked up within seconds of the next appointment booking.
Quick-reference cheat sheet
# First time only
make install # install deps
cp .env.example .env.local && nano .env.local
make up # start Docker (Postgres + Redis + Langfuse)
make migrate # create DB tables
# Every day β open 4 terminals
make up # terminal 1 (if not already running)
make api # terminal 2
make ui # terminal 3
make worker # terminal 4 (only needed for reminders)
# Checks
curl localhost:8000/health
make test
make lint
# Reset everything
docker compose down -v && make up && make migrate
# Useful Docker commands
docker compose ps # see container status
docker compose logs -f # tail all logs
docker compose logs postgres # logs for one service