Jan Server is the self-hostable half of the Jan project: a set of Go microservices that expose an OpenAI-compatible API, with MCP tool integration, Keycloak authentication and a Kong gateway in front. It is not a single container, and the deployment reflects that.
The good news is that the project ships a setup wizard that writes the configuration for you. The main decision you make during it is where inference runs.
Before you start
- A VPS with at least 8 GB RAM โ the projectโs stated minimum, with 12 GB recommended for the full service set
- A clean Linux install (Ubuntu 24.04 LTS is a sensible default)
- Docker Engine and Docker Compose v2
makeandgit- A domain name if you want TLS, which you do for anything beyond a test
Most cheap VPS plans are 4 GB and will not do. These clear the floor:
| Provider | Plan | vCPU / RAM / Disk | Price | Link |
|---|---|---|---|---|
| Contabo | Cloud VPS 4 | 4 / 8 GB / 100 GB | 5.50 EUR/mo excl. VAT | Contabo |
| Hetzner Cloud | CX33 | 4 / 8 GB / 80 GB NVMe | 8.49 EUR/mo excl. VAT | Hetzner Cloud |
| DigitalOcean | 8 GB droplet tier | 8 GB | check current rate | DigitalOcean |
| Vultr | 8 GB instance tier | 8 GB | check current rate | Vultr |
| Linode | 8 GB shared tier | 8 GB | check current rate | Linode |
For a fuller breakdown of these plans, see our VPS comparison.
Step 1: Connect and install dependencies
ssh user@your-vps-ip
sudo apt update && sudo apt upgrade -y
sudo apt install -y docker.io docker-compose-v2 make git
sudo systemctl enable --now docker
Confirm Compose v2 is what you got, since Jan Serverโs compose file uses the include: key that v1 does not understand:
docker compose version
Step 2: Clone the repository
Unlike most self-hosted apps, you do not write your own docker-compose.yml here. The repositoryโs compose file pulls in fragments for infrastructure, the API services, MCP tools, the web app and inference.
git clone https://github.com/janhq/server.git
cd server
Step 3: Run the setup wizard
make quickstart
The wizard writes a single root .env and then starts Compose. It asks three things:
- LLM provider โ local vLLM, which downloads models and expects a GPU, or a remote OpenAI-compatible endpoint, which needs neither. On a standard VPS, choose the remote endpoint.
- MCP search provider โ Serper needs an API key, SearXNG runs locally without one, or you can disable search and still keep the MCP tools service.
- Media API โ enable it for uploads, or leave it off for the smallest runtime.
If you cannot run the wizard interactively, copy the template and populate it instead:
cp .env.template .env
nano .env
make setup
make setup checks dependencies, creates directories and pulls the base images without starting anything.
Step 4: Start the stack
make up-full
Once the containers settle you have:
| Service | Port | Purpose |
|---|---|---|
| API Gateway (Kong) | 8000 | The entry point for everything |
| LLM API | 8080 | OpenAI-compatible chat completions |
| Response API | 8082 | Multi-step tool orchestration |
| Media API | 8285 | Uploads and media IDs |
| MCP Tools | 8091 | Search, scraping, code execution |
| Web chat UI | 3001 | The browser client |
| Keycloak | 8085 | Authentication console |
API documentation is served at http://your-vps-ip:8000/api/swagger/index.html.
Step 5: Secure it before you expose it
This is the step to not skip. Two defaults are unsafe on a public server:
- The Keycloak console ships with
admin/admin. Change it immediately at port 8085. Keycloak issues the tokens that Kong validates, so this one credential protects the whole API surface. - Only port 8000 should be reachable from outside. Kong enforces the JWT and API key checks; the service ports behind it do not. Bind the rest to localhost or block them at the firewall.
sudo ufw allow OpenSSH
sudo ufw allow 443/tcp
sudo ufw enable
Then terminate TLS with Caddy, Traefik or Nginx and proxy to port 8000. Leaving an unauthenticated LLM endpoint on the public internet gets it found and used by someone else, usually within days.
Running inference locally
If you did choose local vLLM, the inference fragment defines two profiles. The GPU profile runs vllm/vllm-openai:v0.11.2 with janhq/Jan-v1-4B as the default model and reserves an NVIDIA device, so it will not start on a typical VPS. The CPU profile runs without a GPU and is genuinely usable for testing, but token generation on shared vCPUs is slow enough that you will not want it in front of users.
Renting GPU capacity and pointing Jan Server at it as a remote endpoint is usually cheaper than a GPU server sitting idle between prompts.
Day-to-day operation
Update to a newer release:
git pull
docker compose pull
make up-full
Back up the Postgres volume and your root .env. The .env holds the secrets the wizard generated, and restoring a database without it leaves you with sessions you cannot decrypt.
Check what a service is doing with docker compose logs -f llm-api, substituting the service you care about.
Common problems
| Symptom | Cause |
|---|---|
include is not a valid compose key | Docker Compose v1 is installed; you need v2 |
| Containers are killed during startup | Under 8 GB RAM, the stack gets OOM-killed |
| Every API call returns 401 | Requests are hitting a service port directly rather than Kong on 8000 |
| vLLM container never becomes healthy | The GPU profile is active on a host with no NVIDIA device |
Jan Server asks more of a VPS than a typical self-hosted app, mostly because it brings its own gateway, identity provider and database rather than assuming you have them. Size for 8 GB, put Kong behind TLS, and change the Keycloak password before anything else.
Frequently asked questions
Do I need a GPU to self-host Jan Server on a VPS?
Not if you choose a remote OpenAI-compatible endpoint when the setup wizard asks about your LLM provider. The VPS then runs only the API, orchestration and auth services. The local vLLM option has a GPU profile that expects an NVIDIA card and a CPU profile that works without one at much lower speed.
Why does the Keycloak console accept admin as both username and password?
That is the default credential the compose setup ships with, and it is fine on a laptop but not on a public server. Change it before you open any port to the internet, because the Keycloak realm is what protects every API route behind the Kong gateway.
Which port should I put behind my reverse proxy?
Port 8000, the Kong API gateway. It is the documented entry point and it enforces the JWT and API key checks in front of the individual services. Publishing the service ports directly would let callers bypass the gateway and its authentication entirely.