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

AI App Hosting on VPS for OpenClaw, RAG, and Bots

By Raman Kumar

Share:

Updated on Jul 31, 2026

AI App Hosting on VPS for OpenClaw, RAG, and Bots

Start with the right VPS for AI app hosting on VPS

AI app hosting on VPS works best when you size the server for the workload, not the buzz. A small support bot, a RAG index, a sales assistant, and a webhook worker do not need the same machine as a public demo with steady traffic.

If you need a private setup with APAC-friendly latency, direct support, and migration help, a Hostperl VPS gives you space to keep the app, database, and bot worker under your control.

In this tutorial, you will deploy an OpenClaw-style AI app stack with Docker Compose, PostgreSQL + pgvector, Redis, Nginx, and TLS. The same layout also works for Next.js front ends, FastAPI APIs, NestJS workers, and private AI tools that should stay close to users in New Zealand and across APAC.

  • SERVER_IP = your VPS public IP, for example 203.0.113.10
  • DOMAIN_NAME = the hostname you point to the VPS, for example ai.example.com
  • ADMIN_USER = your sudo account, for example deploy
  • APP_USER = the Linux account that runs the app files, for example openclaw
  • APP_DIR = app directory, for example /opt/openclaw
  • APP_PORT = internal app port, for example 3000

Connect, detect the OS, and create a non-root admin

Start with SSH from your local computer. Keep the first root session open until you confirm the new sudo user works in a second terminal. That gives you a fallback if a firewall or SSH change goes wrong.

ssh root@203.0.113.10

If your provider gives you a default non-root account, use that instead:

ssh ubuntu@203.0.113.10

On the VPS as root, confirm the operating system before you install anything.

cat /etc/os-release

You should see either Ubuntu/Debian or AlmaLinux/Rocky Linux. Use the matching block below.

Ubuntu and Debian

On the VPS as root, create a sudo user, add SSH keys, and prepare a safe login test.

adduser deploy
usermod -aG sudo 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 deploy, grants sudo access, and copies the current authorized key so you can test a second login. If you use a different key path, replace it before copying. Successful output means the commands complete without errors.

Open a second terminal on your local computer and test the new account.

ssh deploy@203.0.113.10
sudo -v
whoami
pwd

You should land in a shell as deploy, and sudo -v should prompt for the password or return quietly if NOPASSWD is already in place. Do not disable root login until this works.

AlmaLinux and Rocky Linux

On the VPS as root, use the wheel group instead of sudo.

useradd -m 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

Now test the new login from a second terminal.

ssh deploy@203.0.113.10
sudo -v
whoami
pwd

Once the new account works, continue with the root session still open in case you need to recover access.

Install Docker, Nginx, and the tools this stack needs

The app in this guide uses Docker Compose so you can keep the bot API, worker, PostgreSQL, and Redis in one deployment. That makes AI app hosting on VPS easier to troubleshoot, and it keeps rollback steps straightforward.

On Hostperl, many customers use this pattern for launch-ready systems that would otherwise be split across Vercel, Render, and a separate database host. If you want a practical comparison, see AI app hosting for Vercel alternatives and private bots and Private AI app hosting for APAC teams in 2026.

Ubuntu and Debian

sudo apt update
sudo apt install -y ca-certificates curl gnupg lsb-release ufw nginx git
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo usermod -aG docker deploy
sudo systemctl enable --now docker nginx
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw --force enable

After this, docker --version, docker compose version, and nginx -v should return version numbers. ufw status should show SSH and Nginx allowed.

AlmaLinux and Rocky Linux

sudo dnf install -y dnf-plugins-core ca-certificates curl git nginx firewalld
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo usermod -aG wheel,docker deploy
sudo systemctl enable --now docker nginx firewalld
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload

On Rocky or AlmaLinux, check that firewalld lists ssh, http, and https. If SELinux is enforcing, keep the default policy and avoid moving the app into unusual paths.

Build the AI app stack with Docker Compose

On the VPS as the non-root sudo user, create the app directory and switch into it.

sudo -iu deploy
mkdir -p /opt/openclaw/app
cd /opt/openclaw
pwd

You should now be inside /opt/openclaw. This keeps app files separate from system files, which matters during migrations and restores.

Create the environment file first. Store API keys here, not in the codebase.

cat > /opt/openclaw/.env <<'EOF'
DOMAIN_NAME=ai.example.com
APP_PORT=3000
POSTGRES_DB=openclaw
POSTGRES_USER=openclaw
POSTGRES_PASSWORD=change-this-password
REDIS_URL=redis://redis:6379/0
OPENAI_API_KEY=replace-with-your-key
JWT_SECRET=replace-with-a-long-random-string
EOF
chmod 600 /opt/openclaw/.env

Restrictive permissions matter here. If another user can read .env, they can reuse your model key and bot credentials.

Next, create a Docker Compose file for the app, PostgreSQL with pgvector, Redis, and a simple health check. This example assumes your app image exposes a health endpoint at /health and reads environment variables from .env. Replace ghcr.io/example/openclaw:latest with your real image.

cat > /opt/openclaw/docker-compose.yml <<'EOF'
services:
  db:
    image: pgvector/pgvector:pg16
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - db_data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    command: ["redis-server", "--appendonly", "yes"]
    volumes:
      - redis_data:/data

  app:
    image: ghcr.io/example/openclaw:latest
    env_file: .env
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    ports:
      - "127.0.0.1:${APP_PORT}:3000"
    restart: unless-stopped

volumes:
  db_data:
  redis_data:
EOF

Run a syntax-style check by asking Compose to render the file. This catches bad indentation and missing variables before startup.

cd /opt/openclaw
docker compose config

If the file is valid, Compose prints the merged configuration instead of an error. That is the point where you can trust the structure enough to start the stack.

Bring the stack up and watch the container status.

docker compose up -d
docker compose ps
docker compose logs --tail=50 db

You want to see all services in running or healthy state. If db fails, the password or environment file is usually the first thing to check.

Add Nginx and TLS for the public endpoint

Put Nginx in front of the app so you can serve HTTPS on port 443, keep the app private on 127.0.0.1, and set headers for webhooks, Next.js front ends, and agent dashboards. This also keeps the reverse proxy separate from the app container, which makes rollback cleaner.

Create the site config on the VPS as root or with sudo.

Ubuntu and Debian

sudo tee /etc/nginx/sites-available/openclaw.conf > /dev/null <<'EOF'
server {
    listen 80;
    server_name ai.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        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
sudo ln -s /etc/nginx/sites-available/openclaw.conf /etc/nginx/sites-enabled/openclaw.conf
sudo nginx -t
sudo systemctl reload nginx

AlmaLinux and Rocky Linux

sudo tee /etc/nginx/conf.d/openclaw.conf > /dev/null <<'EOF'
server {
    listen 80;
    server_name ai.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_http_version 1.1;
        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
sudo nginx -t
sudo systemctl reload nginx

Replace ai.example.com with your real domain. The syntax test must pass before the reload, or Nginx may refuse to serve traffic.

Now issue TLS. If you already pointed DNS at the VPS, Certbot can complete the HTTP challenge.

Ubuntu and Debian

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d ai.example.com

AlmaLinux and Rocky Linux

sudo dnf install -y epel-release certbot python3-certbot-nginx
sudo certbot --nginx -d ai.example.com

When Certbot finishes, it writes the HTTPS server block and schedules renewals. You should see a success message and a test URL.

Run migrations, seed the RAG store, and smoke test the app

Most AI apps need a first-run migration and a small knowledge import. This is where support bots and workflow agents differ from static websites. They need a database schema, a vector index, and a way to prove the app can answer from your own content.

Inside your containerized app, run the migration command your project uses. For a Node.js app that exposes scripts, it may look like this:

cd /opt/openclaw
docker compose exec app npm run migrate
docker compose exec app npm run seed:kb

For FastAPI projects, the equivalent may be Alembic or a custom Python command. The key is to run migrations before opening traffic, then confirm the app can write embeddings into pgvector.

Check the database extension and vector support.

docker compose exec db psql -U openclaw -d openclaw -c 'CREATE EXTENSION IF NOT EXISTS vector;'
docker compose exec db psql -U openclaw -d openclaw -c '\dx'

You should see vector in the extension list. That tells you pgvector is active and ready for retrieval queries.

Now test the public endpoint from the VPS and from your own computer.

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

A healthy app returns 200 or a JSON payload that says the service is ready. If your app exposes a bot test route, submit one controlled prompt and confirm it reaches the right knowledge base.

Control cost, latency, and bot behavior

AI hosting costs rise fastest when every query hits a large model. For support bots and internal workflow agents, cache repeated retrieval results, prefer a smaller model for routine tasks, and log token usage per request. That gives you a real monthly picture instead of a surprise invoice.

On the app side, expose usage fields in logs and store them alongside request IDs. On the hosting side, keep the VPS sized to your actual traffic. A right-sized Hostperl plan is usually better than overspending on a public serverless platform that charges separately for functions, bandwidth, and database calls.

If your team is comparing open-source PaaS tools, read OpenClaw hosting for AI agents and RAG apps and Host AI agent apps with Docker Compose on a VPS for a cleaner migration path.

Backups, rollback, and audit-friendly checks

Before you put customer traffic on the bot, take a backup you can actually restore. For this stack, that means the PostgreSQL volume, the Redis state if you keep it, and the Compose file plus .env.

cd /opt/openclaw
docker compose exec db pg_dump -U openclaw -d openclaw > /opt/openclaw/openclaw.sql
sudo tar -czf /root/openclaw-backup-$(date +%F).tar.gz /opt/openclaw

Keep a recent restore test in your notes. A backup you never restored is only a hope.

For rollback, pin your app image tag instead of using latest, then change the tag in docker-compose.yml and run:

cd /opt/openclaw
docker compose pull
docker compose up -d

That gives you a predictable path back if a new model adapter, webhook handler, or UI release breaks production.

If you want help placing private AI apps close to your users, Hostperl keeps the setup practical: the app, database, and reverse proxy stay on your VPS, and your team can keep control of keys, logs, and backups. Start with a Hostperl VPS for the app layer, then add the right DNS and SSL setup for your domain.

That is usually the cleanest route for support bots, RAG systems, and internal workflow agents that need APAC latency, predictable costs, and a support desk that can actually follow the deployment path.

Verification and troubleshooting

Finish with checks from both the server and your browser. First, confirm the services are running.

docker compose ps
sudo ss -tulpn | grep -E '(:80|:443|:3000)'
sudo journalctl -u nginx --no-pager -n 50

You should see Nginx listening on 80 and 443, while the app stays bound to localhost on 3000. If you reboot, the containers should come back because of restart: unless-stopped and the Docker service enablement.

Problem: Nginx returns 502 Bad Gateway.
Check: docker compose logs --tail=100 app
Clue: the app may not be listening on the port you mapped.
Fix: adjust the container port in docker-compose.yml, then run docker compose up -d.

Problem: pgvector extension is missing.
Check: docker compose exec db psql -U openclaw -d openclaw -c '\dx'
Clue: no vector entry appears.
Fix: run CREATE EXTENSION IF NOT EXISTS vector; again and confirm you are connected to the correct database.

Problem: Certbot fails domain validation.
Check: dig +short ai.example.com
Clue: the DNS record does not point to your VPS.
Fix: update the A record, wait for propagation, then rerun Certbot.

For a final smoke test, submit one request to your bot endpoint from a client machine and confirm the response includes the right knowledge source or workflow action. That tells you the app, database, SSL, and routing all work together. For teams planning the next step, private AI app hosting on VPS for RAG and bots is a natural follow-on once the first deployment is stable.

FAQ

Should I host an AI app on VPS or use Vercel?

If your app needs private keys, persistent workers, a vector database, or predictable monthly cost, a VPS is usually the cleaner choice.

Do I need PostgreSQL and Redis for every bot?

No. Simple bots can run without Redis, but most retrieval apps benefit from PostgreSQL with pgvector and Redis for queues or caching.

Can I keep customer data in APAC?

Yes. A VPS in a regional data center helps keep traffic and stored data closer to New Zealand and APAC users, which helps with latency and policy requirements.

What should I back up first?

Back up the database, the environment file, the Compose file, and any uploaded knowledge files. Those are the items you need most during a restore.