Congressional Districts API - Systemd Deployment Guide¶
This guide covers deploying the Congressional Districts API as a systemd service.
Overview¶
| Service | Port | Description |
|---|---|---|
cbdistricts-api |
32406 | FastAPI data API |
cbdistricts-web |
32405 | FastHTML web frontend |
Prerequisites¶
- Python 3.12+ via pyenv (
~/.pyenv/versions/nominates) - DuckDB database at
data/output/cbdistricts.duckdb - Project installed at
/home/bisenbek/projects/nominate/cbdistricts
Service Files¶
API Service (/etc/systemd/system/cbdistricts-api.service)¶
[Unit]
Description=Congressional Districts API Server
After=network.target
[Service]
Type=simple
User=bisenbek
Group=bisenbek
WorkingDirectory=/home/bisenbek/projects/nominate/cbdistricts
Environment="PATH=/home/bisenbek/.pyenv/versions/nominates/bin:/usr/local/bin:/usr/bin:/bin"
ExecStart=/home/bisenbek/.pyenv/versions/nominates/bin/python -m api.main
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/home/bisenbek/projects/nominate/cbdistricts/data
PrivateTmp=true
[Install]
WantedBy=multi-user.target
Web Service (/etc/systemd/system/cbdistricts-web.service)¶
[Unit]
Description=Congressional Districts Web Server
After=network.target
[Service]
Type=simple
User=bisenbek
Group=bisenbek
WorkingDirectory=/home/bisenbek/projects/nominate/cbdistricts
Environment="PATH=/home/bisenbek/.pyenv/versions/nominates/bin:/usr/local/bin:/usr/bin:/bin"
ExecStart=/home/bisenbek/.pyenv/versions/nominates/bin/python web/server.py
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
# Security hardening
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=read-only
ReadWritePaths=/home/bisenbek/projects/nominate/cbdistricts/data
PrivateTmp=true
[Install]
WantedBy=multi-user.target
Installation¶
# Copy service files
sudo cp docs/cbdistricts-api.service /etc/systemd/system/
sudo cp docs/cbdistricts-web.service /etc/systemd/system/
# Reload systemd
sudo systemctl daemon-reload
# Enable services to start on boot
sudo systemctl enable cbdistricts-api
sudo systemctl enable cbdistricts-web
# Start services
sudo systemctl start cbdistricts-api
sudo systemctl start cbdistricts-web
Management Commands¶
# Check status
sudo systemctl status cbdistricts-api
sudo systemctl status cbdistricts-web
# Start/Stop/Restart
sudo systemctl start cbdistricts-api
sudo systemctl stop cbdistricts-api
sudo systemctl restart cbdistricts-api
# View logs (live)
sudo journalctl -u cbdistricts-api -f
# View logs (last 100 lines)
sudo journalctl -u cbdistricts-api -n 100
# View logs since boot
sudo journalctl -u cbdistricts-api -b
Health Checks¶
Nginx Reverse Proxy (Optional)¶
If fronting with nginx at districts.nominate.ai:
# /etc/nginx/sites-available/districts.nominate.ai
upstream cbdistricts_web {
server 127.0.0.1:32405;
}
upstream cbdistricts_api {
server 127.0.0.1:32406;
}
server {
listen 443 ssl http2;
server_name districts.nominate.ai;
ssl_certificate /etc/letsencrypt/live/districts.nominate.ai/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/districts.nominate.ai/privkey.pem;
# Web frontend
location / {
proxy_pass http://cbdistricts_web;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# API endpoints
location /api/ {
proxy_pass http://cbdistricts_api;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# API docs
location /docs {
proxy_pass http://cbdistricts_api;
proxy_set_header Host $host;
}
location /redoc {
proxy_pass http://cbdistricts_api;
proxy_set_header Host $host;
}
location /openapi.json {
proxy_pass http://cbdistricts_api;
proxy_set_header Host $host;
}
}
server {
listen 80;
server_name districts.nominate.ai;
return 301 https://$server_name$request_uri;
}
Troubleshooting¶
Service fails to start¶
# Check detailed error
sudo journalctl -u cbdistricts-api -n 50 --no-pager
# Verify Python environment
/home/bisenbek/.pyenv/versions/nominates/bin/python -c "import api.main; print('OK')"
# Verify database exists
ls -la /home/bisenbek/projects/nominate/cbdistricts/data/output/cbdistricts.duckdb
Port already in use¶
# Find process using port
sudo lsof -i :32406
sudo lsof -i :32405
# Kill if needed
sudo kill -9 <PID>
Permission issues¶
# Ensure correct ownership
sudo chown -R bisenbek:bisenbek /home/bisenbek/projects/nominate/cbdistricts
# Verify pyenv is accessible
sudo -u bisenbek /home/bisenbek/.pyenv/versions/nominates/bin/python --version
Environment Variables¶
Optional environment variables (set in service file or /etc/environment):
| Variable | Default | Description |
|---|---|---|
API_HOST |
0.0.0.0 |
API bind address |
API_PORT |
32406 |
API port |
API_DEBUG |
false |
Debug mode |
API_DB_MAX_CONNECTIONS |
10 |
Max DB pool connections |
Monitoring¶
Recommended monitoring endpoints:
- Liveness:
GET /api/v1/health- Returns 200 if service is running - Readiness:
GET /api/v1/info- Returns 200 with DB stats if fully operational
Example Prometheus scrape config: