Back to tutorials
Tutorial

Install Ollama on Ubuntu VPS With Nginx and TLS

Install Ollama on a VPS, secure it behind Nginx, add TLS, and lock access to a private LLM endpoint on Ubuntu.

By Anurag Singh
Updated on Oct 18, 2026
Category: Tutorial
Share article
Install Ollama on Ubuntu VPS With Nginx and TLS

Hosting your own LLM endpoint keeps prompts and responses out of third-party logs. It can also reduce jitter because the model sits close to the apps calling it.

This walkthrough shows you how to install Ollama on a VPS, run it under systemd, and publish it through Nginx with HTTPS. You decide exactly what gets exposed.

The steps assume Ubuntu Server LTS on a typical VPS. The same setup also works on a dedicated server, especially if you later add more cores or a GPU.

What you’ll build (and what it is not)

  • Ollama API bound to 127.0.0.1:11434 (not exposed to the public internet).
  • Nginx as the only public entry point, terminating TLS and proxying requests to Ollama.
  • Access controls so you can keep it private (VPN-only, IP allowlist, or HTTP auth).
  • Basic verification via curl and Nginx logs.

This is not a “host a public ChatGPT replacement” guide. Public LLM endpoints get abused fast unless you add auth, rate limits, quotas, and monitoring.

Prerequisites and sizing (CPU/RAM reality check)

Before you touch the install, decide what “fast enough” means for your use case. On CPU-only VPS plans, small models can work well for internal tools and light chatbot traffic.

They just won’t feel instantaneous.

  • OS: Ubuntu Server LTS (root or sudo access)
  • Disk: 30–60 GB free (models are large; keep headroom for logs and updates)
  • CPU-only starting point: 4 vCPU / 8–16 GB RAM for small quantized models
  • Comfortable CPU-only: 8 vCPU / 16–32 GB RAM for better concurrency

As a rough guide, a quantized 7B–8B model often fits comfortably in RAM on a 16 GB VPS. Larger models often push you toward 32 GB+ and can still feel sluggish on CPU.

If you expect multiple concurrent users, prioritize more CPU cores, not only more RAM.

If you need predictable throughput, move to a larger instance sooner rather than later. A HostMyCode VPS is a solid baseline for private endpoints.

Dedicated servers are the next step once CPU stays saturated or you want GPU hardware.

Step 1: Prepare your VPS (updates, user, firewall)

SSH into the server and patch the base system:

sudo apt update
sudo apt -y upgrade
sudo reboot

Reconnect after the reboot.

If you haven’t set up a firewall yet, do it now. In most cases, you only want SSH and HTTPS reachable from the internet:

sudo apt -y install ufw
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status

If you want a fuller walkthrough, see Set Up UFW Firewall on Ubuntu for a Web Server.

For this guide, the critical point is simple: do not open port 11434 to the internet.

Step 2: Install Ollama on Ubuntu

Use the official installer script. It’s the most direct approach on Ubuntu.

Run it via sudo:

curl -fsSL https://ollama.com/install.sh | sudo sh

Check that the service is up:

sudo systemctl status ollama --no-pager

If it isn’t running yet, enable and start it:

sudo systemctl enable --now ollama

Then confirm the local API responds:

curl -s http://127.0.0.1:11434/api/tags | head

You should see JSON (often an empty list until you pull a model).

Step 3: Bind Ollama to localhost (do not expose it)

You want Ollama listening only on 127.0.0.1. The cleanest method is a systemd override that sets OLLAMA_HOST.

Create an override:

sudo systemctl edit ollama

Add this:

[Service]
Environment="OLLAMA_HOST=127.0.0.1:11434"

Reload and restart:

sudo systemctl daemon-reload
sudo systemctl restart ollama

Verify what address it’s listening on:

sudo ss -ltnp | grep 11434 || true

You should see 127.0.0.1:11434. If it’s bound to 0.0.0.0, stop here and fix that before you put Nginx in front.

Step 4: Pull a small model for testing

On a CPU-only VPS, start small. You want to validate the full path (Ollama → Nginx → TLS) without turning “testing” into a long wait.

Pull a model:

ollama pull llama3.2

Then run a quick local prompt:

ollama run llama3.2 "Write a two-sentence status update for a server maintenance window."

If it feels slow, that’s normal on smaller CPU plans. What matters is whether it works for your traffic.

You can tune models and quantizations later. First, get the deployment and exposure rules right.

Step 5: Install and configure Nginx as a reverse proxy

Install Nginx:

sudo apt -y install nginx
sudo systemctl enable --now nginx

Before requesting TLS, create a DNS record. You need a hostname (for example, ollama-api.example.com) pointing to your VPS public IP.

If you manage domains through HostMyCode, you can do this via domains DNS management.

Create an Nginx site config:

sudo nano /etc/nginx/sites-available/ollama-api

Paste this HTTP-only server block (TLS comes next). Replace ollama-api.example.com with your real hostname:

server {
  listen 80;
  server_name ollama-api.example.com;

  access_log /var/log/nginx/ollama-api.access.log;
  error_log  /var/log/nginx/ollama-api.error.log;

  # Basic hardening headers (safe defaults)
  add_header X-Content-Type-Options nosniff always;
  add_header X-Frame-Options DENY always;
  add_header Referrer-Policy no-referrer always;

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

    # Keep connections sane for streaming responses
    proxy_read_timeout 300s;
    proxy_send_timeout 300s;

    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;

    # Ollama can stream tokens; avoid buffering
    proxy_buffering off;
  }
}

Enable the site and test the config:

sudo ln -s /etc/nginx/sites-available/ollama-api /etc/nginx/sites-enabled/ollama-api
sudo nginx -t
sudo systemctl reload nginx

Test from your laptop (or from the VPS itself, using the hostname):

curl -s http://ollama-api.example.com/api/tags | head

Step 6: Add TLS with Let’s Encrypt (Certbot)

Use HTTPS if you’ll ever send prompts, API keys, or internal code snippets. Let’s Encrypt + Certbot is the standard setup.

Install Certbot and the Nginx plugin:

sudo apt -y install certbot python3-certbot-nginx

Request a certificate:

sudo certbot --nginx -d ollama-api.example.com

Certbot will update your Nginx config and set up renewals. Confirm the timer exists:

systemctl list-timers | grep -E 'certbot|letsencrypt' || true

If you need a deeper troubleshooting flow (chain issues, renewal problems, mixed content), use HostMyCode’s guide: TLS setup guide tutorial (2026).

Step 7: Lock down access (pick one approach)

At this point, the endpoint is reachable over the internet. Don’t leave it wide open unless you’re intentionally offering a public API.

Pick one of the patterns below and apply it deliberately.

Option A (recommended): VPN-only access

This works best for admin teams and internal services. Put operators and calling services on a VPN, then restrict Nginx to the VPN subnet.

If you don’t have WireGuard set up yet, follow: Set Up WireGuard VPN on Ubuntu VPS for Admin Access.

Then add an allowlist to your Nginx server block (replace 10.8.0.0/24 with your VPN subnet):

# Inside the server { } block
allow 10.8.0.0/24;
deny all;

Reload Nginx:

sudo nginx -t && sudo systemctl reload nginx

Option B: IP allowlist (quick and effective)

If your office or app servers have stable IPs, allowlist them:

allow 203.0.113.10;
allow 203.0.113.11;
deny all;

Be realistic about the tradeoff. If your ISP changes your IP, you can lock yourself out until you update the config.

Option C: HTTP Basic Auth (works everywhere, not perfect)

Basic Auth blocks casual scanning and drive-by requests. It does not replace proper per-user auth or quotas.

Install tools and create a password file:

sudo apt -y install apache2-utils
sudo htpasswd -c /etc/nginx/.htpasswd ollamauser

Add this to your Nginx location block:

location / {
  auth_basic "Private LLM";
  auth_basic_user_file /etc/nginx/.htpasswd;

  proxy_pass http://127.0.0.1:11434;
  proxy_http_version 1.1;
  proxy_read_timeout 300s;
  proxy_send_timeout 300s;
  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;
  proxy_buffering off;
}

Reload Nginx and test with credentials:

sudo nginx -t && sudo systemctl reload nginx
curl -u ollamauser -s https://ollama-api.example.com/api/tags | head

Step 8: Verify the endpoint behaves exactly as intended

These checks catch the most common “accidentally public” mistakes.

  • Ollama is not reachable on the public IP:
    curl -s http://YOUR_SERVER_PUBLIC_IP:11434/  || true

    This should fail (timeout/connection refused) if you bound to localhost and didn’t open the port.

  • HTTPS works and redirects from HTTP:
    curl -I http://ollama-api.example.com
    curl -I https://ollama-api.example.com
  • Nginx is the only public entry point:
    sudo tail -n 50 /var/log/nginx/ollama-api.access.log
  • Ollama logs look normal:
    sudo journalctl -u ollama -n 100 --no-pager

Step 9: Try a real API call through Nginx

Ollama exposes an HTTP API. Here’s a minimal request sent through your Nginx/TLS endpoint using /api/generate:

curl -s https://ollama-api.example.com/api/generate \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "llama3.2",
    "prompt": "Give me three bullet points to harden an Ubuntu VPS.",
    "stream": false
  }' | jq

If you enabled Basic Auth, add -u user. If you used VPN or IP allowlisting, run the command from an allowed network.

No jq installed? Install it:

sudo apt -y install jq

Operational notes: updates, capacity, and safe exposure

Once the endpoint is reachable, usage tends to creep up. Keep these operator basics in mind:

  • Model storage grows fast. Track /usr/share/ollama (or wherever your install stores models) and keep 20–30% disk free.
  • CPU saturation looks like “slow answers”. Check load and throttling:
    uptime
    htop
  • Nginx timeouts matter. If generations get cut off, raise proxy_read_timeout for long prompts.
  • Don’t skip monitoring. If this endpoint becomes a dependency, alert on CPU, RAM, disk, and Nginx 5xx errors. Use Netdata + uptime checks as a practical baseline.

If you later put Ollama behind an app (chat UI, RAG pipeline, or a coding assistant), keep the same rule. Bind Ollama to localhost.

Expose only Nginx (or your app) to the network.

Common pitfalls (and quick fixes)

  • Certbot fails HTTP validation: make sure DNS points to the VPS, and ports 80/443 are open in UFW and any upstream firewall.
  • 502 Bad Gateway from Nginx: confirm Ollama is running and listening on localhost:
    sudo systemctl status ollama --no-pager
    curl -s http://127.0.0.1:11434/api/tags | head
    sudo ss -ltnp | grep 11434
  • Streaming responses look “stuck”: ensure proxy_buffering off; is set, and increase proxy_read_timeout.
  • It works locally but not remotely: check allow/deny rules, Basic Auth, and that your test machine is on the VPN/allowlisted IP.

Summary: a private LLM endpoint you can actually operate

You’ve installed Ollama under systemd, kept it bound to localhost, and exposed it only through Nginx with HTTPS and explicit access controls.

It’s intentionally boring. That means fewer open ports, fewer surprises, and logs you can find quickly.

If you want a stronger baseline server posture, pair this with HostMyCode’s web server hardening checklist (the TLS and header sections map directly onto this stack).

If your current box bogs down under model load—or you want cleaner isolation for a private AI endpoint—move it to a properly sized VPS. Start with a HostMyCode VPS, or choose managed VPS hosting if you want help with OS updates, hardening, and day-to-day reliability.

FAQ

Should I open port 11434 to the internet?

No. Keep Ollama on 127.0.0.1 and expose it only through Nginx with TLS and access controls. Public 11434 invites scanning and abuse.

Can I run Ollama on a small VPS?

You can, but keep expectations realistic. A small CPU-only VPS may handle a lightweight model for personal use or low traffic. If you need concurrency, move up in vCPU and RAM.

What’s the safest way to make the API private?

VPN-only access is the simplest strong option. Put your team and services on WireGuard, then allow only the VPN subnet in Nginx.

How do I confirm Ollama isn’t exposed directly?

Check ss -ltnp shows 127.0.0.1:11434, and test curl http://PUBLIC_IP:11434 fails. Also confirm UFW does not allow 11434.

Where do I look when requests fail?

Start with /var/log/nginx/ollama-api.error.log and journalctl -u ollama. Most issues are 502s from a stopped service, wrong bind address, or an allowlist/auth rule blocking you.