IPv4 & IPv6 Leasing - Any RIR, Any LocationOrder Now
Hostperl

Deploy a RAG App on openSUSE Leap VPS

By Raman Kumar

Share:

Updated on Sep 5, 2026

Deploy a RAG App on openSUSE Leap VPS

What you are building

This tutorial shows you how to deploy a small production RAG app on openSUSE Leap VPS, behind a reverse proxy, with a local Qdrant vector database, a non-root service account, firewalld, and systemd supervision. The goal is a setup you can hand to a customer: it starts on reboot, exposes only the web port, keeps the database private, and gives you a rollback path if the cutover fails.

If you are sizing the server for a customer project, a Hostperl VPS is usually the cleanest fit for this kind of workload. You get enough control for RAG services, while still having room to move into a larger instance as the document corpus, traffic, or queue depth grows.

This guide uses openSUSE Leap for a reason: the OS choice matters here. Leap ships with YaST, zypper, systemd, firewalld, and AppArmor, all of which are useful in a supportable VPS deployment. The steps below assume a fresh server and one domain name, example.com, pointed at the VPS.

Before you start: architecture and tradeoffs

The app has three parts. Nginx receives public HTTP traffic, the RAG application listens only on 127.0.0.1:8000, and Qdrant listens only on 127.0.0.1:6333. That separation matters because it keeps your vector store off the public network while still letting the app query it locally.

  • Public: Nginx on ports 80 and 443.
  • Private app: Python RAG API on 127.0.0.1:8000.
  • Private vector store: Qdrant on 127.0.0.1:6333.
  • Service user: deploy owns the app files and never runs as root.

For customers who want a predictable launch window, this design keeps the blast radius of a broken update small. If you need help matching this workload to a sized server, Hostperl’s managed VPS hosting is a practical starting point, especially when the workload is still changing and the final memory footprint is not fixed yet.

Connect to the VPS and detect the OS

On your local computer

ssh root@203.0.113.10

The IP address 203.0.113.10 is a documentation example. Replace it with the real public IP assigned to your server.

On the VPS as root

cat /etc/os-release

This confirms you are on openSUSE Leap before you install anything. You should see NAME="openSUSE Leap" in the output.

Create a non-root admin and keep root open

Do not close the root session yet. Create the deploy account first, give it sudo access, then test a second login before you touch SSH hardening.

On the VPS as root

useradd -m -s /bin/bash deploy
passwd deploy
usermod -aG wheel deploy
mkdir -p /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
cp /root/.ssh/authorized_keys /home/deploy/.ssh/authorized_keys
chown -R deploy:deploy /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys

This creates the deploy user, sets a password for initial access, adds it to the wheel group, and copies your current SSH key so you can log in without password prompts.

On your local computer

ssh deploy@203.0.113.10

Open a second terminal and confirm you can log in as deploy. Then test sudo:

On the VPS as the non-root sudo user

sudo -v
sudo whoami
pwd

You should see root from sudo whoami. Keep the original root session open until this works.

Update Leap, install packages, and set time sync

On the VPS as root

zypper refresh
zypper update -y
zypper install -y nginx python3 python3-pip python3-virtualenv qdrant firewalld openssl curl chrony

This refreshes package metadata, updates the base system, and installs the web server, Python runtime, vector database, firewall, TLS tools, and time synchronization service.

systemctl enable --now chronyd
systemctl status chronyd --no-pager

Leap typically uses chrony for clock accuracy. You want it running before you issue certificates or compare log timestamps.

Prepare the application directory and virtual environment

On the VPS as the non-root sudo user

sudo mkdir -p /opt/myapp
sudo chown deploy:deploy /opt/myapp
cd /opt/myapp
python3 -m virtualenv .venv
. .venv/bin/activate
python -V
pip install --upgrade pip
pip install fastapi uvicorn qdrant-client pydantic

This creates the app directory in the standard location used throughout Hostperl support workflows, then builds an isolated Python environment so system packages stay clean. The version check should show a current Python 3 release from Leap.

Now create the application file.

On the VPS as the non-root sudo user

cat > /opt/myapp/app.py <<'EOF'
from fastapi import FastAPI
from qdrant_client import QdrantClient
from qdrant_client.http import models

app = FastAPI()
client = QdrantClient(url="http://127.0.0.1:6333")
COLLECTION = "docs"

@app.get("/health")
def health():
    return {"status": "ok"}

@app.post("/init")
def init_collection():
    client.recreate_collection(
        collection_name=COLLECTION,
        vectors_config=models.VectorParams(size=3, distance=models.Distance.COSINE),
    )
    return {"collection": COLLECTION, "status": "created"}

@app.post("/seed")
def seed():
    client.upsert(
        collection_name=COLLECTION,
        points=[
            models.PointStruct(id=1, vector=[1.0, 0.0, 0.0], payload={"text": "VPS launch checklist"}),
            models.PointStruct(id=2, vector=[0.0, 1.0, 0.0], payload={"text": "Backup restore drill"}),
        ],
    )
    return {"status": "seeded"}

@app.get("/search")
def search():
    results = client.search(collection_name=COLLECTION, query_vector=[1.0, 0.0, 0.0], limit=2)
    return {"matches": [r.payload for r in results]}
EOF

This is a minimal but real RAG-style service: it creates a collection, inserts sample vectors, and searches them. Keep the example simple so you can validate the infrastructure before you add your own ingestion pipeline.

Run Qdrant privately with systemd

Qdrant on openSUSE Leap should listen only on localhost for this design. Create an override file that keeps it off the public interface.

On the VPS as root

systemctl cat qdrant

Check the packaged unit first so you know where Leap stores the service definition.

Now create a drop-in for the private bind settings.

mkdir -p /etc/systemd/system/qdrant.service.d
cat > /etc/systemd/system/qdrant.service.d/override.conf <<'EOF'
[Service]
Environment="QDRANT__SERVICE__HTTP_PORT=6333"
Environment="QDRANT__SERVICE__GRPC_PORT=6334"
Environment="QDRANT__SERVICE__HOST=127.0.0.1"
EOF
systemctl daemon-reload
systemctl enable --now qdrant
systemctl status qdrant --no-pager

You should see Qdrant active and bound to localhost. If the packaged unit uses different environment keys on your Leap release, check the service logs before changing anything else.

Create the systemd service for the RAG app

On the VPS as root

cat > /etc/systemd/system/ragapp.service <<'EOF'
[Unit]
Description=RAG App
After=network.target qdrant.service
Wants=qdrant.service

[Service]
Type=simple
User=deploy
Group=deploy
WorkingDirectory=/opt/myapp
Environment="PATH=/opt/myapp/.venv/bin"
ExecStart=/opt/myapp/.venv/bin/uvicorn app:app --host 127.0.0.1 --port 8000
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true

[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now ragapp
systemctl status ragapp --no-pager

This unit starts the app after networking and Qdrant are ready. The hardening flags are conservative and fit a simple API service that only needs its working directory and a loopback port.

Open the firewall safely

On the VPS as root

systemctl enable --now firewalld
firewall-cmd --permanent --add-service=ssh
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
firewall-cmd --list-all

This keeps SSH open, then adds web traffic before any future lock-down step. Qdrant and the app remain private because they only listen on localhost.

Configure Nginx as the public reverse proxy

On the VPS as root

cat > /etc/nginx/conf.d/ragapp.conf <<'EOF'
server {
    listen 80;
    server_name example.com www.example.com;

    location / {
        proxy_pass http://127.0.0.1:8000;
        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;
    }
}
EOF
nginx -t
systemctl enable --now nginx
systemctl reload nginx

Replace example.com with your real domain. The syntax test must pass before you reload. If it fails, read the exact line number in the output and fix that first.

If your service logs are noisy, Hostperl’s Nginx log rotation and app log debugging guide is still useful as a pattern, even though it was written for Ubuntu. The log locations differ, but the troubleshooting sequence is the same.

Enable TLS with Let’s Encrypt

Leap can use certbot from packages or your preferred repository source. After the package is installed, request the certificate and let Nginx reload it safely.

On the VPS as root

zypper install -y certbot python3-certbot-nginx
certbot --nginx -d example.com -d www.example.com

Choose the redirect option when prompted. Certbot should validate the domain and update the Nginx server block.

Initialize the vector collection and test the app

On the VPS as the non-root sudo user

curl -s http://127.0.0.1:8000/health
curl -s -X POST http://127.0.0.1:8000/init
curl -s -X POST http://127.0.0.1:8000/seed
curl -s http://127.0.0.1:8000/search

The health endpoint should return {"status":"ok"}. The init and seed calls create test data, and the search call should return the seeded documents.

On your local computer

curl -I https://example.com
curl https://example.com/health

The first command should show an HTTP 200 or a redirect to HTTPS, depending on your redirect settings. The second should return the health JSON through the public proxy.

Check logs, ports, and reboot persistence

On the VPS as root

ss -tulpn | grep -E '(:80|:443|:8000|:6333)'
journalctl -u ragapp -n 50 --no-pager
journalctl -u qdrant -n 50 --no-pager
systemctl is-enabled ragapp
systemctl is-enabled qdrant
systemctl is-enabled nginx

You want to see only Nginx exposed on public ports, with the app and Qdrant bound to localhost. The enabled checks confirm the stack will return after a reboot.

Rollback and recovery

If the app deploy fails after a code change, stop at the service layer first. Do not touch the firewall or TLS settings unless the issue is network-related.

On the VPS as root

cp /opt/myapp/app.py /opt/myapp/app.py.bak
systemctl stop ragapp
mv /opt/myapp/app.py.bak /opt/myapp/app.py
systemctl start ragapp
systemctl status ragapp --no-pager

That restores the last working app file and brings the service back. If Qdrant is the problem, check whether the collection exists and confirm the loopback bind is still in place before exposing anything else.

Troubleshooting the most likely failures

App will not start

Run:

journalctl -u ragapp -xe --no-pager

Look for Python import errors or permission denied messages. The usual fix is a missing package in the virtualenv or a typo in ExecStart. Correct the file, then run systemctl daemon-reload and restart the service.

Qdrant is unreachable

Run:

journalctl -u qdrant -xe --no-pager
ss -ltnp | grep 6333

You should see Qdrant listening on 127.0.0.1:6333. If it is not, adjust the service override and reload systemd.

Nginx returns 502

Run:

journalctl -u nginx -xe --no-pager
curl -v http://127.0.0.1:8000/health

A healthy local curl means the app is alive, and the problem is usually the proxy target or a syntax issue in the Nginx server block. Re-run nginx -t before reloading.

If you want to run a RAG app for a customer, Hostperl can supply the VPS capacity and the operational headroom to keep it stable during launch and growth. Start with a Hostperl VPS, then expand only when your vector store, document ingestion, and response time data justify it.

For teams that want managed help during cutovers or recovery work, Hostperl support can help you plan the move and reduce downtime risk.

FAQ

Can I expose Qdrant publicly?
You can, but this tutorial keeps it private on localhost so the app is the only public entry point. That is the safer default for small production deployments.

Why use openSUSE Leap for this?
Leap gives you a stable systemd, firewalld, and AppArmor base. That works well for a VPS that needs predictable maintenance rather than rapid package churn.

Can I swap Nginx for another web server?
Yes, but this guide is written for Nginx only. Keep the reverse proxy private-to-public split the same even if you change the frontend web server.

What should I monitor after launch?
Watch service health, memory use, app response time, Qdrant disk growth, and journal errors. Those are the first signals that a customer-facing RAG service is drifting out of its safe range.