Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Agent Backend

FastAPI backend with Gunicorn/Uvicorn, Celery, Redis, SQLModel, and Alembic.

Local development

Install uv, then run:

cp .env.example .env
uv sync
uv run alembic upgrade head
make dev

The API is available at http://localhost:8000. Start Redis and the Celery worker in a second terminal if background jobs are needed:

make worker

Deploy to a VPS with systemd and Nginx

The following guide targets a current Ubuntu or Debian server. It uses:

  • /opt/agent-backend for the application
  • agent-backend as a dedicated system user
  • Gunicorn with the uvicorn-worker ASGI worker
  • systemd for the API and Celery processes
  • Nginx as the public reverse proxy
  • Certbot for HTTPS

Replace api.example.com with the API domain throughout this guide. Point the domain's DNS A/AAAA record at the VPS before requesting a certificate.

1. Install system packages and uv

Log in with a sudo-capable account:

sudo apt update
sudo apt install -y curl git make nginx redis-server snapd
curl -LsSf https://astral.sh/uv/install.sh | sudo env UV_INSTALL_DIR=/usr/local/bin sh
uv --version

Enable Redis now:

sudo systemctl enable --now redis-server
sudo systemctl status redis-server --no-pager

2. Create the service user and clone the application

sudo useradd \
  --system \
  --create-home \
  --home-dir /var/lib/agent-backend \
  --shell /usr/sbin/nologin \
  agent-backend

sudo install -d \
  -o agent-backend \
  -g agent-backend \
  /opt/agent-backend

sudo -u agent-backend -H git clone \
  https://github.com/Devscale-Indonesia/agent-backend.git \
  /opt/agent-backend

For a private repository, configure a read-only deploy key for the agent-backend user or upload a release artifact instead of cloning over HTTPS.

Install the exact locked production dependencies:

cd /opt/agent-backend
sudo -u agent-backend -H /usr/local/bin/uv sync --locked --no-dev

uv installs the compatible Python version automatically when the server does not already provide it.

3. Configure production environment variables

cd /opt/agent-backend
sudo cp .env.example .env
sudo nano .env

At minimum, review and replace these values:

APP_NAME=Agent Backend API
DEBUG=false

# Four slashes make this an absolute SQLite path.
DATABASE_URL=sqlite:////opt/agent-backend/app.db

# Use the frontend origin, not the API origin. Add more values if required.
CORS_ORIGINS=["https://app.example.com"]

TAVILY_API_KEY=replace-me
LITELLM_BASE_URL=
LITELLM_API_KEY=replace-me
LITELLM_MODEL=gpt-5.4-mini

AUTH_SECRET_KEY=replace-with-a-long-random-secret
AUTH_ALGORITHM=HS256
AUTH_ACCESS_TOKEN_EXPIRE_MINUTES=60

CELERY_BROKER_URL=redis://localhost:6379/0
CELERY_RESULT_BACKEND=redis://localhost:6379/1

Generate a suitable authentication secret with openssl rand -hex 32, paste the output into AUTH_SECRET_KEY, and then protect the file:

sudo chown agent-backend:agent-backend /opt/agent-backend/.env
sudo chmod 600 /opt/agent-backend/.env

Run the database migrations as the service user:

cd /opt/agent-backend
sudo -u agent-backend -H .venv/bin/alembic upgrade head

4. Create the API systemd service

Create /etc/systemd/system/agent-backend.service:

[Unit]
Description=Agent Backend API
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=agent-backend
Group=agent-backend
WorkingDirectory=/opt/agent-backend
Environment=PYTHONUNBUFFERED=1
UMask=0077
ExecStart=/opt/agent-backend/.venv/bin/gunicorn app.main:app \
    --worker-class uvicorn_worker.UvicornWorker \
    --workers 1 \
    --bind 127.0.0.1:8000 \
    --access-logfile - \
    --error-logfile -
Restart=on-failure
RestartSec=5
TimeoutStopSec=30
PrivateTmp=true
NoNewPrivileges=true

[Install]
WantedBy=multi-user.target

The service uses one web worker because the repository currently defaults to SQLite. Multiple processes writing to SQLite can cause lock contention. Keep this at one until the application is configured and tested with a production database server and its appropriate async driver.

The process binds only to 127.0.0.1; external clients must go through Nginx. Pydantic loads /opt/agent-backend/.env because systemd sets that directory as WorkingDirectory.

5. Create the Celery systemd service

Create /etc/systemd/system/agent-backend-worker.service:

[Unit]
Description=Agent Backend Celery Worker
After=network-online.target redis-server.service
Wants=network-online.target
Requires=redis-server.service

[Service]
Type=simple
User=agent-backend
Group=agent-backend
WorkingDirectory=/opt/agent-backend
Environment=PYTHONUNBUFFERED=1
UMask=0077
ExecStart=/opt/agent-backend/.venv/bin/celery \
    -A app.worker:celery_app \
    worker \
    --loglevel=info
Restart=on-failure
RestartSec=5
TimeoutStopSec=30
PrivateTmp=true
NoNewPrivileges=true

[Install]
WantedBy=multi-user.target

If Redis is hosted on another server, update the two Redis URLs in .env and remove Requires=redis-server.service and redis-server.service from After.

Load and start both units:

sudo systemctl daemon-reload
sudo systemctl enable --now agent-backend.service
sudo systemctl enable --now agent-backend-worker.service

sudo systemctl status agent-backend.service --no-pager
sudo systemctl status agent-backend-worker.service --no-pager
curl http://127.0.0.1:8000/health

The health endpoint should return:

{"status":"ok"}

View live logs with:

sudo journalctl -u agent-backend.service -f
sudo journalctl -u agent-backend-worker.service -f

6. Configure Nginx

Create /etc/nginx/sites-available/agent-backend:

server {
    listen 80;
    listen [::]:80;
    server_name api.example.com;

    client_max_body_size 10m;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;

        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;

        # Chat responses are streamed; send chunks to clients immediately.
        proxy_buffering off;
        proxy_cache off;
        proxy_read_timeout 300s;
        proxy_send_timeout 300s;
    }
}

Enable the site and validate the configuration before reloading:

sudo ln -s /etc/nginx/sites-available/agent-backend \
  /etc/nginx/sites-enabled/agent-backend
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl enable --now nginx
sudo systemctl reload nginx
curl http://api.example.com/health

7. Configure the firewall

If UFW is used, allow SSH before enabling it so the current session is not locked out. If SSH uses a custom port, allow that port instead of the standard OpenSSH profile.

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo ufw status

Port 8000 does not need to be opened because Gunicorn listens only on the loopback interface.

8. Enable HTTPS with Certbot

After DNS resolves to the VPS and HTTP works:

sudo snap install --classic certbot
sudo ln -sf /snap/bin/certbot /usr/local/bin/certbot
sudo certbot --nginx -d api.example.com
sudo certbot renew --dry-run

Certbot edits the Nginx server block for TLS and installs automatic renewal. Afterward, verify https://api.example.com/health and update CORS_ORIGINS if the frontend production URL changed.

Deploy updates

Pull only fast-forward changes, sync the locked dependencies, run migrations, and restart both processes:

cd /opt/agent-backend
sudo -u agent-backend -H git pull --ff-only origin main
sudo -u agent-backend -H /usr/local/bin/uv sync --locked --no-dev
sudo -u agent-backend -H .venv/bin/alembic upgrade head
sudo systemctl restart agent-backend.service agent-backend-worker.service
sudo systemctl status agent-backend.service --no-pager
sudo systemctl status agent-backend-worker.service --no-pager
curl https://api.example.com/health

Troubleshooting

For 502 Bad Gateway, first confirm that the API is running and listening:

sudo systemctl status agent-backend.service --no-pager
sudo journalctl -u agent-backend.service -n 100 --no-pager
curl -v http://127.0.0.1:8000/health
sudo tail -n 100 /var/log/nginx/error.log

For failed background jobs, check Redis and the Celery worker:

sudo systemctl status redis-server agent-backend-worker.service --no-pager
sudo journalctl -u agent-backend-worker.service -n 100 --no-pager
redis-cli ping

redis-cli ping should return PONG.

References

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages