V
VPS Self-Hosting Field Guide
Hosting & DevOps · Beginner-friendly · ELI5 explanations included

Self-host your bots, APIs & AI workers on a VPS.

A beginner-friendly field guide for moving an existing bot, API, webhook, or AI worker to an always-on internet computer. Every major technical term is explained in plain English first, then shown in its proper technical form.

Built as a practical, beginner-friendly guide for anyone learning how to self-host bots, APIs, webhooks, and AI workers. The screenshots are from a real TierHive VPS setup, with account/public endpoint details hidden.

THE SIMPLE MENTAL MODEL
Your app keeps running even when your laptop is off.
24 / 7remote runtime
Dockerrepeatable deployment
Persistentdata, logs & backups
Portablenot tied to one PaaS
01 · Foundation

What exactly is a VPS?

ELI5: A VPS is basically a small computer you rent on the internet. Instead of keeping your laptop awake so your bot or API stays online, you let that rented computer do the job 24/7.
🖥️

Think: a computer you rent on the internet

A Virtual Private Server (VPS) is a small always-on Linux computer in a data center. You connect to it using SSH — basically a secure remote terminal — install what your app needs, and keep your code running there.

Localhost means “this machine.” On your laptop, localhost is your laptop. On the VPS, localhost is the VPS itself.
📦

Docker makes the server predictable

Instead of manually installing every Node/Python dependency on the VPS, Docker packages the app runtime into a container. Think of Docker as a sealed lunchbox containing your app and everything it needs; the container is that lunchbox while it is running. A Compose file describes how it starts, restarts, mounts storage, and exposes ports.

Goal: the same app that works locally can be recreated on another VPS with minimal host-specific work.
Your laptop
admin only
Internet
SSH / HTTPS
VPS
Ubuntu
Docker
bot / API / worker
02 · Why VPS

Why not just keep it on a laptop or PaaS?

ELI5: Local PC = your own computer must stay on. Managed PaaS (managed hosting) = a company runs most server chores for you. VPS = you rent the server and get more control, but you also handle more of the setup and maintenance.
QuestionLocal PCManaged PaaS (managed hosting)Your VPS
Runs while your laptop is off?NoYesYes
Beginner convenienceEasyEasiestMore setup
OS / networking controlFull localLimitedFull
Host several small services togetherPossible, PC must stay onUsually billed per serviceYes, within VPS resources
Persistent files / SQLiteLocal diskNeeds provider volumeYou control the disk
Custom domains / reverse proxyExtra tunnelingUsually built inFull control
Maintenance responsibilityYour PCMostly providerYou patch, secure, back up
💸

Consolidation

A single VPS can host multiple lightweight bots, APIs, webhooks, and workers — as long as CPU/RAM/disk stay healthy.

🔐

Control

You decide firewall rules, storage paths, Docker networks, reverse proxy, backups, and update timing.

🧳

Portability

Docker + Git + environment variables make it easier to move from one VPS provider to another.

🧠

AI-friendly

Great for API-based AI agents, background workers, queues, vector services, and webhook orchestration without keeping a workstation on.

03 · Choose the traffic pattern

Bot, API, or AI worker?

The deployment is similar, but networking changes depending on who initiates the connection.

ELI5: The biggest question is: does the internet need to call your app, or does your app only call other services? A Discord bot often calls outward. A public API or webhook needs people/services to call inward.

Usually outbound-only (your bot calls out)

The bot connects outward to Discord/Slack. You typically do not need to expose the app port publicly.

A private health endpoint can be bound to 127.0.0.1 so only the VPS itself can reach it.

Critical token rule

For a single production bot token, avoid running two production instances at the same time unless the app is explicitly designed for sharding/HA.

Cutover rule: validate the new host offline as much as possible, stop the old live process, then start the new one.

Inbound traffic is required (the internet calls your app)

Clients must reach your service. The safer pattern is Internet → HTTPS 443 → Caddy/Nginx → app on localhost.

Domain + TLS

Point DNS to the VPS (or use your provider’s NAT/HAProxy feature), then let a reverse proxy handle HTTPS certificates and forward traffic to the container.

Most AI workers call APIs outward

Gemini/OpenAI/Anthropic calls are outbound HTTPS. If the agent also receives webhooks, treat its inbound side like an API.

Watch memory and concurrency

API-based AI is light on the VPS; local model inference is not. A 2 GB VPS is suitable for small orchestration services, not large on-box LLMs.

04 · Before touching production

Inventory the app first.

ELI5: Before moving house, make a list of your boxes. For an app, the “boxes” are the code, secret keys, database/files, ports, and the way you test that it is alive.
A · CODE

Where is the source?

GitHub/GitLab repo, local folder, Docker image, or provider-connected repository?

B · SECRETS

What must stay private?

Bot tokens, API keys, database URLs, webhook secrets, OAuth credentials. Never paste them into docs or screenshots.

C · DATA

What must survive?

SQLite files, uploads, generated files, logs, vector indexes, user data, and existing backups.

D · NETWORK

Does it need inbound traffic?

Bots may not. APIs/webhooks do. Note every port and domain currently in use.

E · HEALTH

How do we know it works?

HTTP /health//ready, bot health command, test request, log message, or worker heartbeat.

F · ROLLBACK

How do we go back?

Keep the old host intact and stopped for a short rollback window. Do not delete it on cutover day.

Golden migration rule: make a verified backup before the final cutover, and keep the old environment available until the VPS has been stable long enough for you to trust it.
05 · Create the server

Provision a small Ubuntu VPS.

The exact provider can be TierHive, Hetzner, DigitalOcean, Vultr, Linode, AWS Lightsail, or another VPS host.

ELI5: RAM is short-term working memory. CPU is the worker doing calculations. Disk is long-term storage. Region is where the rented computer physically lives. For a small bot/API, 2 GB RAM and 1 vCPU is a comfortable beginner starting point.

Safe starter profile

OS: Ubuntu 24.04 LTS (or current provider-supported LTS)

RAM: 2 GB for small bots/APIs

Disk: 20–30 GB NVMe

CPU: 1 vCPU is enough for many lightweight Node/Python services

Region: near your users/external services

Login: SSH key preferred over password

Scale based on real usage, not guesses. Check free -h, df -h, and docker stats after launch.
Sanitized TierHive Create VPS screen
Real TierHive example: Ubuntu, 2 GB RAM, 25 GB NVMe, Singapore, SSH key selected. Account details are hidden.
Sanitized VPS provisioning screen
Provisioning phase: the provider builds the virtual machine and boots the OS.
Sanitized TierHive running VPS screen
Running state: verify CPU, RAM, disk, location, OS, and how SSH access is exposed. Public endpoint is hidden here.
06 · Server basics

Connect, patch, and install Docker.

Commands below are intentionally shown one at a time. Replace placeholders inside <...>.

ELI5: SSH is like opening a secure remote keyboard and screen for the VPS. Firewall is the security guard deciding which network doors are allowed. Port is a numbered network door — for example, SSH normally uses port 22.
1

SSH into the VPS

Run
Terminal / PowerShell
ssh <admin-user>@<public-host-or-ip> -p <ssh-port>

Some providers use port 22 directly. NAT-based providers may give you an external forwarded port that maps to internal SSH port 22.

2

Patch Ubuntu

Run
Ubuntu
sudo apt update
Then
Ubuntu
sudo apt upgrade -y
3

Install Docker + Compose

Run
Ubuntu
sudo apt install -y docker.io docker-compose-v2
Then
Ubuntu
sudo systemctl enable --now docker
Optional
Allow current admin user to run Docker
sudo usermod -aG docker $USER
The docker group is effectively root-level access. Only add trusted admin users. Log out and back in after changing group membership.
4

Enable a simple firewall

Run
Ubuntu
sudo ufw allow 22/tcp
Then
Ubuntu
sudo ufw enable
Do not lock yourself out. If your provider forwards an external port to internal SSH port 22, UFW normally needs to allow the internal service port (22), not necessarily the external NAT port. Confirm your provider’s networking model first.
5

Optional: add swap on a small VPS

Swap is disk space used as emergency overflow when RAM gets tight. It is much slower than real RAM, so treat it as a safety cushion, not a replacement for RAM. A 2 GB swapfile is common on a 2 GB starter VPS.

1
sudo fallocate -l 2G /swapfile
2
sudo chmod 600 /swapfile
3
sudo mkswap /swapfile
4
sudo swapon /swapfile
5
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
VPS console showing Ubuntu login
Provider console is your emergency view of the server. Normal daily administration should be through SSH.
07 · App preparation

Make the application portable.

ELI5: Repository (repo) = the project folder tracked by Git. .env = a private settings file for secrets like tokens and API keys. Persistent storage = folders that survive even if the Docker container is rebuilt or replaced.

Recommended repository shape

Project layout
your-app/
├── Dockerfile
├── compose.yaml
├── .dockerignore
├── .env.example
├── package.json / requirements.txt
├── src/
└── scripts/

Keep runtime data outside the Git repository. The repository should be replaceable without deleting production data.

Persistent host layout

VPS layout
~/apps/your-app/
├── app/        ← Git clone / source
└── runtime/
    ├── data/
    ├── backups/
    └── logs/

This separation lets you rebuild the container or pull new code while keeping the database and backups on the host disk.

.env

Production secrets live on the VPS, not Git. Restrict with chmod 600 .env.

.dockerignore

Exclude .env, databases, runtime folders, backups, logs, and node_modules from image builds.

Health check

Provide a low-cost readiness check or app-native status command so deployment success is measurable.

How do I move the source?

Clone with SSH deploy key or your authenticated Git setup
git clone [email protected]:<org-or-user>/<repo>.git ~/apps/your-app/app
For production, a dedicated read-only deploy key is a clean option. Never copy your personal private Git key into documentation.

Best long-term move: put the deployable code in a private Git repository. For a one-time transfer, use scp or rsync — but still keep secrets/data separate.

Export the current environment variables and download the persistent data using that host’s supported method. The provider can be Railway, Render, Fly.io, a cPanel server, another VPS, or anything else — the same principles apply.

Do not assume every host stores data in the same path. Identify the live data path before downloading anything.
08 · Docker deployment

Build, validate, start, verify.

ELI5: Docker image = the packaged recipe. Container = the running app made from that recipe. Docker Compose = a small instruction sheet that says which container to run, what settings to load, which folders to keep, and which ports to connect.

Bot / worker Compose pattern

compose.yaml
services:
  app:
    build: .
    restart: unless-stopped
    env_file: .env
    volumes:
      - ../runtime/data:/app/data
      - ../runtime/backups:/app/backups
      - ../runtime/logs:/app/logs

    # Optional local-only health endpoint:
    ports:
      - "127.0.0.1:3000:3000"

If the bot has no HTTP health server, omit the ports block entirely.

API Compose pattern

compose.yaml
services:
  api:
    build: .
    restart: unless-stopped
    env_file: .env
    volumes:
      - ../runtime/data:/app/data
    ports:
      - "127.0.0.1:3000:3000"

Bind to localhost and let a reverse proxy expose HTTPS publicly. This keeps the raw application port off the public internet.

1

Validate Compose without printing secrets

VPS
docker compose config --quiet
2

Build the image

VPS
docker compose build
3

Start in the background

VPS
docker compose up -d
4

Check container status

VPS
docker compose ps
5

Read recent logs if anything looks wrong

VPS
docker compose logs --tail=100
6

Test a local readiness endpoint

VPS
curl -i http://127.0.0.1:3000/ready

Use your app’s real endpoint. A readiness check is the app saying “I am not only running — I am actually ready to do work.” A healthy service commonly answers with HTTP 200, which simply means “OK.”

09 · APIs & webhooks only

Add a domain and HTTPS safely.

Skip this section for a pure outbound Discord bot that does not need a public HTTP endpoint.

ELI5: DNS is the internet’s phonebook: it points a name like api.example.com to your server. HTTPS/TLS encrypts the conversation. Reverse proxy is the receptionist at the front desk: it receives public requests, then forwards them to the correct private app port.
Client / webhook
HTTPS :443
Caddy
127.0.0.1:3000

Caddy example

After DNS points to your reachable VPS endpoint and ports 80/443 can reach the server:

/etc/caddy/Caddyfile
api.example.com {
    reverse_proxy 127.0.0.1:3000
}
Caddy can automatically request/renew TLS certificates when the domain resolves correctly and inbound networking is configured.

Provider networking matters

Some VPS providers give a direct public IPv4. Others use NAT, forwarded ports, HAProxy domains, or provider-managed ingress. Configure the method your host supports.

Do not blindly open every Docker port. Docker networking can interact with host firewall rules in ways beginners may not expect. Prefer localhost bindings + a reverse proxy for app ports.
10 · The important part

Do a safe production cutover.

ELI5: A migration cutover is like moving a store to a new building. First copy the stock, then close the old store, open the new one, check everything, and keep the old location untouched for a while in case you need to go back.
1. Validate the VPS as far as possible without becoming the live instance.Build image, validate config, create storage, run offline preflight, test backup restore in a safe context.
2. Put the old production app into maintenance if it supports it.This reduces last-second data changes.
3. Create the final backup and verify it.For SQLite, run integrity checks and record the file hash.
4. Copy the final backup to the VPS and compare SHA-256 hashes.A SHA-256 hash is like a digital fingerprint for a file. If the fingerprint matches before and after transfer, the file is byte-for-byte identical.
5. Stop the old production process.Especially important for bots using the same token and single-file databases.
6. Restore final data on the VPS, then start the VPS instance.Do not restore over an actively-writing database unless the application explicitly supports online restore.
7. Verify readiness + real behavior.Container healthy, HTTP ready, bot connected, permissions correct, data count correct, and one harmless functional smoke test.
8. Create a new post-cutover backup.This is your first clean restore point from the new host.
9. Keep the old host stopped, not deleted.Retain a rollback window. Delete the old service only when the new host has earned your trust.

SHA-256 on Linux

VPS
sha256sum /path/to/backup.sqlite

SHA-256 on Windows PowerShell

PowerShell
Get-FileHash "C:\path\backup.sqlite" -Algorithm SHA256
Single-instance safety: if two copies use the same Discord token and the same logical production data, you can get duplicate behavior, race conditions, conflicting schedulers, or corrupt assumptions. Treat “exactly one live production instance” as a default safety rule unless your architecture explicitly supports multiple replicas.
11 · After launch

Operate it like a tiny production server.

ELI5: After deployment, you mostly watch three things: Is the app running? Is the server running out of RAM/disk? Are your backups recent and restorable?

Containers

Status
docker compose ps
Recent logs
docker compose logs --tail=100

Server resources

RAM
free -h
Disk
df -h
Live containers
docker stats

Backups

Automate local backups, verify them periodically, and copy at least one recent backup off the VPS. A backup that has never been tested is only a hope.

Keep secrets and production data out of Git. Use an off-server backup destination for disaster recovery.

Simple update workflow

Backup
git pull
build
up -d
For schema changes, command registration changes, or risky releases, use the application’s documented migration/release procedure — not a blind “pull and restart.”
12 · Troubleshooting

When something fails, check in this order.

ELI5 troubleshooting rule: Check the simplest layer first: server on → container running → app started → network reachable → app logic/data correct. Do not change five things at once.

Run docker compose ps, then docker compose logs --tail=100. Look for missing environment variables, permission errors, database errors, port conflicts, or startup checks that have not finished yet.

Confirm the VPS is running in the provider dashboard, confirm the correct public endpoint/forwarded port, and use the provider console if necessary. A firewall or SSH configuration change can lock you out.

Check bot permissions, guild/server IDs, command registration, maintenance mode, and whether a second production instance is still running somewhere else.

Check DNS, provider NAT/ingress, ports 80/443, reverse-proxy service status, TLS issuance, and firewall rules. Do not change the app to bind publicly just to “make it work” unless you understand the security impact.

The database was likely stored in the container’s ephemeral filesystem instead of a persistent host mount/volume. Stop writes, find the last valid backup, fix the mount, then restore offline.

Use docker stats and free -h. Check application memory leaks, concurrency, queue buildup, log growth, and whether the VPS is simply undersized. Swap can soften spikes but does not fix a leak.

Real migration · passed
ELI5: This case study proves the process in a real bot migration: copy the app safely, preserve the database, run only one live bot, verify health, and keep backups both on the server and somewhere else.

MarkHQ: managed host → TierHive VPS

A live Discord/AI assistant was migrated to a 2 GB Ubuntu VPS using Docker, persistent SQLite storage, backups, and health checks.

SERVER

Ubuntu 24.04

2 GB RAM · 25 GB NVMe · Singapore · Docker + Compose.

DATA

SQLite preserved

Final snapshot verified before cutover, restored offline, then a new post-cutover backup was created and verified.

NETWORK

Health kept private

Container health server listened internally; host publication was bound to 127.0.0.1:3000 rather than exposed publicly.

Production validation

✅ HTTP readiness returned 200

✅ Discord connected

✅ Database integrity OK

✅ 3 active workspaces present

✅ Required permissions present

✅ Deployment preflight: no warnings

Recovery posture

✅ Post-cutover backup verified

✅ Off-server copy transferred to Windows PC

✅ SHA-256 matched on both machines

✅ Old host stopped but retained for rollback

✅ Temporary transfer copy of secrets removed after success

Why this case matters: the exact provider is not the lesson. The repeatable pattern is: inventory → backup → secure VPS → Docker → persistent storage → offline restore → one-instance cutover → health checks → post-cutover backup → rollback window.
14 · ELI5 Jargon Buster

Technical words, translated into normal human language.

You do not need to memorize these before starting. Use this section as a cheat sheet while you work.

VPSA rented computer on the internet that stays on even when your laptop is off.
ServerA computer whose job is to provide something to other computers — a bot, website, API, file, or service.
SSHA secure way to open a terminal on another computer over the internet.
Linux / UbuntuThe operating system running on the VPS, similar in role to Windows on your PC.
DockerA tool for packaging and running apps in predictable isolated boxes.
Docker imageThe packaged recipe/template used to create a running container.
ContainerYour app running inside Docker. Think “running instance of the packaged app.”
Docker ComposeA configuration file that tells Docker what services to run and how they connect.
Localhost / 127.0.0.1“This same computer only.” On a VPS, localhost means the VPS itself.
PortA numbered network door. Different apps/services listen on different doors.
FirewallThe security guard that decides which network doors are allowed or blocked.
DNSThe internet phonebook that maps a domain name to a server address.
DomainA human-friendly internet name such as api.example.com.
HTTPS / TLSThe encrypted “locked envelope” used when browsers/services talk securely over the web.
Reverse proxyA receptionist that receives public web traffic and forwards it to the correct private app.
Environment variableA setting passed to the app, often used for secrets and configuration.
.env fileA private text file that stores environment variables locally on the server.
Persistent storageData that remains even if your container is replaced, rebuilt, or restarted.
SQLiteA database stored mainly in one file instead of needing a separate database server.
BackupA copy of important data that you can restore if something breaks.
SHA-256 hashA digital fingerprint used to confirm two files are exactly the same.
Health checkA quick “Are you alive?” test for the application.
Readiness checkA stricter “Are you actually ready to handle real work?” test.
HTTP 200A normal web response code that means the request succeeded.
APIA controlled way for software to talk to other software.
WebhookA message one service automatically sends to another when something happens.
Git repositoryA project folder whose file history is tracked by Git.
DeployPut a tested version of your app onto the server so it can run there.
RollbackGo back to the last known working version after a bad deployment.
CutoverThe moment you stop the old live app and make the new server the official live one.
Outbound trafficYour app starts the connection to another service.
Inbound trafficAnother user/service starts a connection to your app.
PaaS / managed hostingA hosting platform that handles much of the server setup for you.
vCPUA virtual CPU core — roughly one unit of processor capacity assigned to your VPS.
RAMFast temporary working memory used while apps are running.
SwapSlower disk space used as emergency overflow when RAM gets tight.
Shortcut: whenever you see a term you do not know, ask three questions: “What is it?”, “Why do I need it?”, and “What breaks if I configure it wrong?”
15 · Quick-start runbook

Use this as your migration runbook.

Check items as you complete them. Your browser remembers this checklist locally.

You are done when: the new service is healthy, the data is correct, the old service is not competing with it, a verified recovery copy exists off-server, and you know exactly how to update or roll back.