A step-by-step walkthrough for wrapping an AI model in a FastAPI endpoint and shipping it to a $5/month VPS you actually control, no serverless cold starts, no per-invocation billing surprises.
At some point, every AI side project reaches the same wall: it works perfectly in a Jupyter notebook or a local script, and then someone asks “can I actually use this?” That question means you need an endpoint, something running continuously, reachable from outside your laptop, that takes a request and returns a model’s response.
The instinct for a lot of people is to reach for a serverless platform. That’s a reasonable choice for very bursty workloads, but it comes with real costs that don’t show up until later: cold starts hurting latency the moment traffic goes quiet, per-invocation pricing that gets expensive once usage is sustained, and painful limits on long-running requests, which matters a lot if a single model call takes 20 or 30 seconds. A small VPS, by contrast, costs roughly $5 a month, gives you predictable latency, and lets you run background workers and persistent connections without fighting the platform. For a first deployment, it’s usually the simpler and cheaper path.
What You’re Actually Building
The goal here is deliberately minimal: a FastAPI app with a single /ask endpoint that takes a prompt, calls an AI model, and returns the response, packaged with Docker, and deployed to a VPS with a clean, repeatable process. No agent framework, no LangChain, no CrewAI. Frameworks earn their place once you need tool calls, memory, or multi-step planning, but on a first deployment they mostly hide what’s actually happening under the hood. Understanding the plain version first makes everything you add later easier to debug.
Step 1: Picking a VPS That Actually Fits
Here’s the thing most people get wrong before they even start: unless you’re running the model itself locally on the server, you don’t need a GPU or 32GB of RAM. If your app is calling an API, OpenAI, Anthropic, OpenRouter, or a local Ollama instance elsewhere, your VPS is doing something much smaller: holding request state, running your web server, and orchestrating the call to whichever model backend you’re using.
For that kind of workload, a basic shared-CPU VPS with 1-2GB of RAM is genuinely enough. Providers like Hostinger offer entry-level VPS plans around $5/month that cover this comfortably, and Hetzner’s shared CPU plans are widely considered excellent value if you’re deploying in Europe. If you’ve never deployed a Linux service before, a provider with a simpler onboarding flow will save you more time than the few dollars you’d save going with the cheapest possible option.
If you do want to run a small quantized model directly on the box instead of calling an external API, that changes the math. A model like a 3B parameter LLaMA variant needs roughly 3GB of RAM to run via Ollama or llama.cpp, meaning you’d want to size up slightly, but this guide focuses on the API-calling pattern, which is the simpler and more common starting point.
Step 2: The Minimal FastAPI App
Here’s what a genuinely minimal wrapper looks like, using the OpenAI SDK as the example client, though this pattern swaps freely for Anthropic, OpenRouter, or any OpenAI-compatible endpoint:
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from openai import OpenAI
import os
app = FastAPI(title="My First AI Endpoint")
client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
SYSTEM_PROMPT = "You are a concise, helpful assistant."
class AskRequest(BaseModel):
prompt: str
@app.post("/ask")
def ask(request: AskRequest):
try:
response = client.chat.completions.create(
model="gpt-4.1-mini",
messages=[
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": request.prompt},
],
)
return {"answer": response.choices[0].message.content}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
That’s genuinely the whole thing. A request comes in, gets validated by Pydantic, gets forwarded to the model, and the response comes back as JSON. Everything you add from here, authentication, rate limiting, streaming responses, is an addition to this core, not a replacement for it.
Step 3: Packaging It With Docker
Docker matters here for one specific reason: it makes your deployment reproducible. Instead of manually installing Python versions and dependencies on the server and hoping they match your local setup, you ship the exact same environment every time.
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
With a requirements.txt containing fastapi, uvicorn, openai, and pydantic, this builds into a self-contained image you can run identically on your laptop and on the VPS.
Step 4: Getting It Onto the Server
Once your VPS is provisioned, the general flow looks like this:
- Install Docker on the VPS. Most providers offer a one-click Docker image at provisioning time, which skips manual setup entirely.
- Copy your project to the server, either via
git cloneif your code is in a repository, orscpfor a quick first deploy. - Set your environment variables, particularly your API key, ideally through an
.envfile that’s excluded from version control rather than hardcoded. - Build and run the container:
docker build -t my-ai-app .
docker run -d -p 80:8000 --env-file .env --name ai-app my-ai-app
At this point, hitting http://your-server-ip/ask with a POST request and a prompt should return a model response. That’s a working, publicly reachable AI endpoint running on infrastructure you actually control.
Step 5: Making Updates Painless
A deployment you have to manually rebuild and restart every time you change a line of code gets tedious fast, and tedious deployments are the ones people stop maintaining. A simple pattern that scales well from a hobby project upward is Git-based auto-deploy: pushing to your main branch triggers a script on the server (via a webhook or a lightweight CI runner) that pulls the latest code, rebuilds the container, and restarts it, ideally keeping the previous image around for a one-command rollback if something breaks.
You don’t need a complex CI/CD pipeline for this on day one. Even a basic shell script triggered by a Git webhook covers the core need: push code, see it live, without SSH-ing in and typing the same four commands every time.
What This Setup Is (and Isn’t) Ready For
This deployment pattern comfortably handles low-to-moderate traffic: prototypes, internal tools, small-scale RAG pipelines, embedding generation, and classifier APIs. It’s a genuinely solid place to run something real without overpaying for infrastructure you don’t need yet.
Where it starts to strain is high-volume, latency-sensitive inference at scale, that’s when GPU infrastructure and dedicated inference platforms start making more sense than a $5 shared-CPU box. But for a first deployment, and for a large share of real production side projects, this setup is not a toy. It’s a legitimate, cheap, and fully controlled way to run an AI-backed API in production.
Conclusion
The jump from “it works on my machine” to “it works on the internet” is smaller than it looks, and it doesn’t require an agent framework, a serverless platform, or a complicated CI pipeline to get there. A minimal FastAPI wrapper, a Dockerfile, and a $5 VPS are enough to get a real, publicly reachable AI endpoint running today, with a clear, incremental path to add authentication, monitoring, and auto-deploys as the project actually needs them. Start simple, understand exactly what’s running and why, and add complexity only when a real requirement demands it, not before.
FAQs
Do I need a GPU on my VPS to run an AI model app? No, not if your app is calling an external API like OpenAI, Anthropic, or OpenRouter. A GPU only becomes necessary if you’re running the model itself directly on the server, for example a local LLM through Ollama.
Is a $5 VPS actually enough for a real AI application? Yes, for low-to-moderate traffic workloads like prototypes, internal tools, embedding generation, and small RAG pipelines. High-volume, latency-critical inference at scale is where you’d eventually need to upgrade to dedicated or GPU infrastructure.
Why use Docker instead of just installing Python directly on the server? Docker makes your deployment reproducible. It guarantees the exact same environment runs on your local machine and on the server, avoiding the classic “it works locally but breaks in production” problem caused by mismatched dependency versions.
Why choose a VPS over a serverless platform for this? Serverless works well for very bursty traffic, but it introduces cold-start latency and per-invocation costs that add up with sustained traffic, and it often struggles with long-running requests. A VPS offers predictable, flat-rate pricing and no cold starts.
How do I keep my API key secure when deploying this way? Store it in an environment file (.env) that’s excluded from version control via .gitignore, and pass it into your Docker container at runtime using --env-file, rather than hardcoding it directly into your application code.
