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

RAG Hosting on openSUSE Leap with pgvector and Qdrant

By Raman Kumar

Share:

Updated on Oct 27, 2026

RAG Hosting on openSUSE Leap with pgvector and Qdrant

What you are building

This tutorial sets up RAG hosting on openSUSE Leap for a small production deployment: one application server, PostgreSQL with pgvector, and a separate Qdrant vector service. That split works well for real hosting setups because it keeps database backups straightforward, lets you restart the app without touching search data, and gives support teams clear failure points during a migration or launch window.

You will install the required packages, create a non-root administrator, secure SSH access, configure the firewall, start PostgreSQL and Qdrant, create a minimal RAG schema, and test both retrieval and persistence. The example uses Hostperl VPS infrastructure from Hostperl VPS, which is a practical fit for early-stage RAG workloads before you move to dedicated hardware.

If you are planning a higher-traffic deployment or need tighter isolation for embeddings and retrieval traffic, Hostperl also offers dedicated server hosting for larger datasets and stricter resource control.

Example architecture and design choices

This build keeps the application code outside the vector store. The app talks to PostgreSQL on 127.0.0.1:5432 and Qdrant on 127.0.0.1:6333 through local sockets or localhost-bound TCP. That reduces exposure, avoids unnecessary public ports, and makes logs easier to read when someone reports a search regression after a deploy.

  • PostgreSQL + pgvector stores structured data, metadata, and smaller embedding sets.
  • Qdrant handles vector search and filtering for faster retrieval at larger scale.
  • openSUSE Leap gives you a stable base with systemd, zypper, firewalld, and AppArmor.

For a simple launch, you can use only PostgreSQL with pgvector. Add Qdrant when you need separate indexing, larger collections, or a cleaner migration path. That is the pattern used here, because it gives you room to grow without forcing a rebuild later.

Open the server and identify the OS

On your local computer, connect to the server with the documented example IP first.

ssh root@203.0.113.10

203.0.113.10 is a documentation example. Replace it with the real public IP assigned to your Hostperl server. If your provider gives you a default non-root account, use that login first and keep the same server IP.

Once you are on the server, check the operating system before installing anything.

On the VPS as root:

cat /etc/os-release

You should see openSUSE Leap release details. If you are on a different Linux distribution, stop here and follow a distribution-specific procedure instead. This guide is written for openSUSE Leap, which is the least-covered OS track for this run.

Create a non-root administrator and keep root open

Do not close your root SSH session yet. Open a second terminal after the new account is ready, then test login and sudo before changing any SSH restrictions.

On the VPS as root, create the admin user, set a password, and add it to the sudo-capable wheel group.

useradd -m -s /bin/bash deploy
passwd deploy
usermod -aG wheel deploy

This creates the deploy account, assigns a shell, and grants elevated access through wheel. Replace deploy only if you already use another standard admin name in your environment.

Now prepare SSH access for that account. If you already have a public key on your laptop, copy its contents into the server.

mkdir -p /home/deploy/.ssh
chmod 700 /home/deploy/.ssh
nano /home/deploy/.ssh/authorized_keys

Paste your public key into the editor, save the file, and exit. In nano, press Ctrl+O, Enter, then Ctrl+X.

Lock the permissions down so SSH accepts the key.

chown -R deploy:deploy /home/deploy/.ssh
chmod 600 /home/deploy/.ssh/authorized_keys

Open a second terminal from your local computer and test the new login.

ssh deploy@203.0.113.10

After login, confirm sudo works.

sudo -v
whoami
id

You should see deploy as the user, and sudo should prompt for the password or accept your key-based session depending on your setup. Keep the original root session open until this works.

Update packages and install the RAG stack

On the VPS as the non-root sudo user, refresh package metadata and install PostgreSQL, Qdrant, and the tools needed for secure service management.

sudo zypper refresh
sudo zypper update -y
sudo zypper install -y postgresql16-server postgresql16-contrib qdrant firewalld curl nano

The package names can vary slightly by Leap release. If your repositories provide a different PostgreSQL major version, install the available -server and -contrib pair, then confirm the version with psql --version.

Check that the key services are available and enabled on the system.

systemctl list-unit-files | grep -E 'postgresql|qdrant|firewalld'

That output should show systemd unit files for the database, vector service, and firewall daemon.

Enable the firewall before exposing anything

Open the required ports before you start the services, then bind the services locally so you do not create a public database by accident.

On the VPS as the non-root sudo user:

sudo systemctl enable --now firewalld
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-port=6333/tcp
sudo firewall-cmd --permanent --add-port=5432/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --list-all

This keeps SSH open and allows the vector and database ports if you later decide to bind them beyond localhost. For this tutorial, we will still constrain both services to loopback for safer defaults. If your app and database stay on the same host, the firewall rules are mostly insurance for future changes and operational clarity.

Initialize PostgreSQL with pgvector

openSUSE packages install PostgreSQL, but you still need to initialize the cluster and start it. Then you will create a database and enable the vector extension.

On the VPS as the non-root sudo user:

sudo /usr/lib/postgresql16/bin/initdb -D /var/lib/pgsql/data
sudo systemctl enable --now postgresql
sudo systemctl status postgresql --no-pager

If your install path differs, check the package file list with rpm -ql postgresql16-server | grep initdb and adjust the path accordingly. A healthy status output should show the service as active.

Set a password for the PostgreSQL superuser and create the application database.

sudo -iu postgres psql -c "ALTER USER postgres WITH PASSWORD 'ChangeThisPostgresPassword!';"
sudo -iu postgres createdb ragapp

Now create the extension and an application user with limited privileges.

sudo -iu postgres psql -d ragapp <<'SQL'
CREATE EXTENSION IF NOT EXISTS vector;
CREATE USER raguser WITH PASSWORD 'ChangeThisRagPassword!';
GRANT ALL PRIVILEGES ON DATABASE ragapp TO raguser;
SQL

That password text is only an example. Replace both passwords immediately with secrets you store securely. For a production deployment, use a secret manager or at minimum a root-readable environment file with strict permissions.

Verify that pgvector is available.

sudo -iu postgres psql -d ragapp -c "\dx"

You should see the vector extension in the list.

Start Qdrant and keep it local

Qdrant is useful when your retrieval layer needs filtering, separate indexing, or easier scaling later. On a fresh single-node deployment, keep it local first.

On the VPS as the non-root sudo user:

sudo systemctl enable --now qdrant
sudo systemctl status qdrant --no-pager

Check the listening address next. You want to see it bound to localhost or a controlled interface, not the public internet.

ss -tulpn | grep 6333

If Qdrant binds too broadly, edit its configuration file. On openSUSE Leap, the package typically places config under /etc/qdrant.

sudo ls -l /etc/qdrant
sudo nano /etc/qdrant/config.yaml

Use the file to bind the service to localhost if the default configuration does not already do that. A safe minimal example looks like this:

service:
  http_port: 6333
  grpc_port: 6334
  host: 127.0.0.1
storage:
  storage_path: /var/lib/qdrant/storage

Save and exit, then restart Qdrant.

sudo systemctl restart qdrant
sudo systemctl status qdrant --no-pager

If the service fails to start, read the journal immediately.

sudo journalctl -u qdrant -n 50 --no-pager

That usually points to a YAML typo, a permission problem, or a bad storage path.

Bind PostgreSQL safely and create the schema

For a host with both services on one machine, PostgreSQL can also remain local-only. Confirm its listening state before moving on.

ss -tulpn | grep 5432

If PostgreSQL listens on all interfaces, restrict it. Open the configuration and adjust the listener.

sudo nano /var/lib/pgsql/data/postgresql.conf

Set or confirm these lines:

listen_addresses = '127.0.0.1'
port = 5432

Then restart PostgreSQL and confirm it comes back cleanly.

sudo systemctl restart postgresql
sudo systemctl status postgresql --no-pager

Now create a table that stores text chunks and embeddings. This example uses vector(1536), which matches many common embedding sizes, but you should adjust the dimension to your model.

sudo -iu postgres psql -d ragapp <<'SQL'
CREATE TABLE IF NOT EXISTS documents (
  id bigserial PRIMARY KEY,
  source text NOT NULL,
  chunk text NOT NULL,
  embedding vector(1536),
  created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS documents_embedding_idx ON documents USING ivfflat (embedding vector_cosine_ops) WITH (lists = 100);
SQL

That index improves similarity search, but it only helps after the table has enough rows. It is still the right default for a production-ready RAG schema.

Write a small smoke-test client

To prove the stack works, create a minimal Python utility that inserts one vector into PostgreSQL and one record into Qdrant, then reads both back. This is enough to validate networking, permissions, and persistence before you connect the app layer.

On the VPS as the non-root sudo user:

sudo zypper install -y python311 python311-pip python311-psycopg2
mkdir -p /opt/myapp
cd /opt/myapp
nano rag_smoke_test.py

Paste this content into the file:

import psycopg2
from psycopg2.extras import Json
import requests

PG_CONN = {
    "host": "127.0.0.1",
    "dbname": "ragapp",
    "user": "raguser",
    "password": "ChangeThisRagPassword!",
}

VECTOR = [0.01] * 1536
TEXT = "Hostperl RAG smoke test"

conn = psycopg2.connect(**PG_CONN)
conn.autocommit = True
cur = conn.cursor()
cur.execute("INSERT INTO documents (source, chunk, embedding) VALUES (%s, %s, %s)", ("smoke", TEXT, VECTOR))
cur.execute("SELECT id, source FROM documents ORDER BY id DESC LIMIT 1")
print("postgres", cur.fetchone())

qdrant = requests.put(
    "http://127.0.0.1:6333/collections/ragtest",
    json={"vectors": {"size": 1536, "distance": "Cosine"}},
    timeout=10,
)
print("qdrant create", qdrant.status_code)

upsert = requests.put(
    "http://127.0.0.1:6333/collections/ragtest/points",
    json={
        "points": [
            {
                "id": 1,
                "vector": VECTOR,
                "payload": {"source": "smoke", "text": TEXT}
            }
        ]
    },
    timeout=10,
)
print("qdrant upsert", upsert.status_code)

readback = requests.post(
    "http://127.0.0.1:6333/collections/ragtest/points/search",
    json={"vector": VECTOR, "limit": 1},
    timeout=10,
)
print("qdrant search", readback.status_code, readback.json())

Save and exit, then install the Python dependency and run the test.

python3 -m pip install --user requests
python3 /opt/myapp/rag_smoke_test.py

Successful output should show a PostgreSQL row, Qdrant collection creation with HTTP 200 or 201, and a search result containing your test payload.

Harden the service account and secrets

Once the stack works, move your sensitive values into an environment file that only root can read. This keeps credentials out of shell history and gives you a clean handoff path if support needs to inspect the app later.

On the VPS as root:

install -d -m 750 -o root -g root /etc/ragapp
nano /etc/ragapp/ragapp.env

Place this content inside the file:

PGHOST=127.0.0.1
PGPORT=5432
PGDATABASE=ragapp
PGUSER=raguser
PGPASSWORD=ChangeThisRagPassword!
QDRANT_URL=http://127.0.0.1:6333

Then lock permissions so the file is not readable by other users.

chmod 600 /etc/ragapp/ragapp.env
chown root:root /etc/ragapp/ragapp.env

You can now point your application service at this file. If you are deploying an actual bot, API, or retrieval service, this pattern keeps the runbook simple and the secret surface small.

Persist the stack across reboot

Reboot persistence matters because the first outage often reveals hidden operational problems. Check that both services survive a restart and return on their own.

On the VPS as the non-root sudo user:

sudo systemctl enable postgresql qdrant firewalld
sudo reboot

After the machine comes back, reconnect from your local computer and verify the services.

ssh deploy@203.0.113.10

Then run:

systemctl is-active postgresql
systemctl is-active qdrant
systemctl is-active firewalld
ss -tulpn | grep -E '5432|6333'

All three services should report active, and the listeners should reappear.

Recovery path and rollback

If the deployment breaks, your rollback should be simple. First, stop the app layer, then preserve data, and only then change packages or configs. For a failed Qdrant change, restore the prior config file and restart the service.

sudo cp /etc/qdrant/config.yaml /etc/qdrant/config.yaml.broken.$(date +%F)
sudo cp /etc/qdrant/config.yaml.bak /etc/qdrant/config.yaml
sudo systemctl restart qdrant

If PostgreSQL changes caused the issue, revert the last config edit and restart it in the same way. For schema mistakes, keep a plain SQL backup before altering tables:

sudo -iu postgres pg_dump ragapp > /var/tmp/ragapp-$(date +%F).sql

That file is enough to rebuild the database on the same server or a replacement Hostperl VPS if you need a quick migration.

Troubleshooting the most likely failures

1. SSH login fails for deploy

Run:

sudo tail -n 50 /var/log/messages
sudo ls -ld /home/deploy /home/deploy/.ssh /home/deploy/.ssh/authorized_keys

Look for bad permissions or an incorrect key file. Fix with:

sudo chown -R deploy:deploy /home/deploy/.ssh
sudo chmod 700 /home/deploy/.ssh
sudo chmod 600 /home/deploy/.ssh/authorized_keys

2. PostgreSQL will not start

Run:

sudo journalctl -u postgresql -n 50 --no-pager

The clue is usually a malformed postgresql.conf line or an initdb path mismatch. Correct the file, then restart PostgreSQL.

3. Qdrant returns connection refused

Run:

sudo journalctl -u qdrant -n 50 --no-pager
ss -tulpn | grep 6333

If it is not listening, the YAML likely has a syntax problem. Repair the config, then restart the service.

4. The vector query works but results look wrong

Check the embedding size in PostgreSQL and Qdrant.

sudo -iu postgres psql -d ragapp -c "\d documents"
curl -s http://127.0.0.1:6333/collections/ragtest | head

Both services must use the same vector dimension. A mismatch silently ruins retrieval quality.

If you want a solid base for RAG hosting, Hostperl VPS gives you the control to run PostgreSQL, pgvector, and Qdrant on your terms. For larger datasets, stronger CPU headroom, or heavier retrieval traffic, move the same design to dedicated server hosting without changing the operational model.

That makes migrations simpler, and it keeps your support path straightforward when you need to scale the app, the database, or both.

FAQ

Should I use pgvector alone or add Qdrant?

Use pgvector alone if your dataset is modest and your application already depends on PostgreSQL. Add Qdrant when retrieval traffic grows, you need richer filtering, or you want search workloads isolated from your primary database.

Can I expose PostgreSQL and Qdrant publicly?

You can, but it is not the right default. Keep both on localhost unless you have a separate private network, strict access controls, and a clear reason to expose them.

What embedding size should I use?

Match the size used by your embedding model. The schema and Qdrant collection must use the same dimension, or similarity search will fail or return bad matches.

What is the safest way to move this setup to another server?

Dump PostgreSQL, copy the Qdrant storage directory if you need the existing index, and reproduce the same service files and environment file on the new host. Test the restore before switching DNS or traffic.

Final check: from the server, confirm services are active and ports are listening. From your local computer, run one SSH login and one smoke test against the app endpoint you add later. That is the minimum standard for a supportable RAG deployment on Hostperl infrastructure.