# Linux server tutorials

Step-by-step tutorials for Linux servers, each tested on the distributions it names. Feed: https://vpsnine.com/tutorials/feed.xml

## Linux basics

- [AlmaLinux vs Rocky Linux: which should you choose for a server?](https://vpsnine.com/tutorials/almalinux-vs-rocky-linux.md) (beginner, 8 min): AlmaLinux and Rocky Linux both rebuild Red Hat Enterprise Linux for free. What actually differs, which runs your software, and how to pick one in a minute.
- [Cron vs systemd timers: how to schedule jobs on Linux](https://vpsnine.com/tutorials/cron-vs-systemd-timers.md) (beginner, 20 min): Schedule a recurring job both ways, with a crontab line and with a systemd timer, and pick the right one for logging, missed runs and random delays.
- [How to create a sudo user and disable root login on Ubuntu and Debian](https://vpsnine.com/tutorials/create-a-sudo-user-ubuntu-debian.md) (beginner, 15 min): Add a normal user with sudo rights on Ubuntu or Debian, give it your SSH key, then stop root logging in over SSH without locking yourself out.
- [How to create a sudo user on AlmaLinux and Rocky Linux](https://vpsnine.com/tutorials/create-a-sudo-user-almalinux-rocky.md) (beginner, 15 min): Add a user to the wheel group on AlmaLinux or Rocky Linux, give it your SSH key with the right SELinux labels, and turn off root logins over SSH.
- [How to find what's using disk space on Linux and free it safely](https://vpsnine.com/tutorials/free-up-disk-space-linux.md) (beginner, 15 min): Find what filled your server's disk with df, du and ncdu, then safely clean package caches, logs, old kernels and deleted files still held open.
- [How to read logs with journalctl](https://vpsnine.com/tutorials/read-logs-with-journalctl.md) (beginner, 15 min): Find the log lines you need with journalctl, by service, time, priority and boot, keep logs across reboots, and cap how much disk the journal uses.
- [How to set the time zone, hostname and NTP on a Linux server](https://vpsnine.com/tutorials/set-timezone-and-ntp-linux.md) (beginner, 10 min): Set a server's time zone with timedatectl, give it a proper hostname, and keep its clock in sync with chrony or systemd-timesyncd, then check it really is.
- [Use curl and jq with the VPSNine API](https://vpsnine.com/tutorials/use-curl-and-jq-with-the-vpsnine-api.md) (beginner, 20 min): Drive the VPSNine API from a shell with curl and jq: filter servers, take snapshots and wait for them, read errors, page invoices, edit the firewall.
- [How to troubleshoot high CPU and memory usage on a Linux server](https://vpsnine.com/tutorials/troubleshoot-high-cpu-and-memory.md) (intermediate, 20 min): Find the process behind high CPU or memory use with top, ps, vmstat and pidstat, read load average and steal time correctly, and spot the OOM killer.
- [How to upgrade Debian 12 (bookworm) to Debian 13 (trixie)](https://vpsnine.com/tutorials/upgrade-debian-12-to-13.md) (intermediate, 45 min): Upgrade a Debian 12 server to Debian 13 in place, the way the release notes describe, with a snapshot to fall back on and checks before and after.
- [How to upgrade Ubuntu 22.04 to 24.04 LTS safely](https://vpsnine.com/tutorials/upgrade-ubuntu-22-04-to-24-04.md) (intermediate, 45 min): Upgrade an Ubuntu 22.04 server to 24.04 LTS in place with do-release-upgrade, with a snapshot first, a backup SSH port, and checks for what changes.
- [How to write a systemd service for your app](https://vpsnine.com/tutorials/create-a-systemd-service.md) (intermediate, 20 min): Run your own program as a systemd service that starts at boot, restarts when it crashes, runs as its own user, reads an env file and is sandboxed.

## Security

- [How to check open ports on Linux with ss and nmap](https://vpsnine.com/tutorials/check-open-ports-linux.md) (beginner, 10 min): List the ports your server listens on with ss, find which program owns each one, then scan the server from outside with nmap to see what the internet can reach.
- [How to configure firewalld on AlmaLinux and Rocky Linux](https://vpsnine.com/tutorials/firewalld-almalinux-rocky.md) (beginner, 20 min): Turn on firewalld, open services and ports for good, limit SSH to your own addresses with a zone, and use rich rules, without locking yourself out.
- [How to create a sudo user and disable root login on Ubuntu and Debian](https://vpsnine.com/tutorials/create-a-sudo-user-ubuntu-debian.md) (beginner, 15 min): Add a normal user with sudo rights on Ubuntu or Debian, give it your SSH key, then stop root logging in over SSH without locking yourself out.
- [How to create a sudo user on AlmaLinux and Rocky Linux](https://vpsnine.com/tutorials/create-a-sudo-user-almalinux-rocky.md) (beginner, 15 min): Add a user to the wheel group on AlmaLinux or Rocky Linux, give it your SSH key with the right SELinux labels, and turn off root logins over SSH.
- [How to get a Let's Encrypt wildcard certificate with the DNS challenge](https://vpsnine.com/tutorials/letsencrypt-wildcard-certificate-dns-challenge.md) (intermediate, 25 min): Get a free *.example.com certificate from Let's Encrypt with certbot or acme.sh and your DNS provider's API, renew it automatically and reload your web server.
- [How to install and secure Redis on Ubuntu 24.04](https://vpsnine.com/tutorials/install-redis-ubuntu.md) (intermediate, 20 min): Install Redis on Ubuntu 24.04, keep it on localhost, replace the open default user with ACL users, block dangerous commands and cap its memory.
- [How to install CrowdSec on Ubuntu and Debian](https://vpsnine.com/tutorials/install-crowdsec.md) (intermediate, 20 min): Install the CrowdSec engine and its nftables firewall bouncer, watch SSH and nginx logs, allowlist your own addresses, and see what it blocks.
- [How to set up a WireGuard VPN server on AlmaLinux and Rocky Linux](https://vpsnine.com/tutorials/wireguard-vpn-server-almalinux.md) (intermediate, 30 min): Install WireGuard on AlmaLinux or Rocky Linux, write wg0.conf, route clients through the server with firewalld masquerading, and connect your devices.
- [How to set up a WireGuard VPN server on Ubuntu 24.04](https://vpsnine.com/tutorials/wireguard-vpn-server-ubuntu.md) (intermediate, 30 min): Install WireGuard on Ubuntu 24.04, write the server config, route clients' traffic through it and connect a laptop and a phone.
- [Manage VPSNine servers with Ansible](https://vpsnine.com/tutorials/manage-vpsnine-servers-with-ansible.md) (intermediate, 35 min): Build an Ansible inventory from the VPSNine API, grouped by tag, plan and image, and run a playbook that patches servers and locks SSH to keys.
- [Self-host Vaultwarden safely with Docker and HTTPS](https://vpsnine.com/tutorials/self-host-vaultwarden.md) (intermediate, 30 min): Run Vaultwarden, the Bitwarden-compatible password server, in Docker behind Caddy with HTTPS, sign-ups closed, a hashed admin token and backups you can restore.

## Networking and VPN

- [How to check open ports on Linux with ss and nmap](https://vpsnine.com/tutorials/check-open-ports-linux.md) (beginner, 10 min): List the ports your server listens on with ss, find which program owns each one, then scan the server from outside with nmap to see what the internet can reach.
- [How to configure firewalld on AlmaLinux and Rocky Linux](https://vpsnine.com/tutorials/firewalld-almalinux-rocky.md) (beginner, 20 min): Turn on firewalld, open services and ports for good, limit SSH to your own addresses with a zone, and use rich rules, without locking yourself out.
- [How to point a domain at your VPS (A and AAAA records)](https://vpsnine.com/tutorials/point-a-domain-to-your-vps.md) (beginner, 15 min): Point your domain at your server with A and AAAA records, add www, pick a sensible TTL, and check with dig that the change has really taken effect.
- [How to send email from a VPS through an SMTP relay](https://vpsnine.com/tutorials/send-email-from-vps-smtp-relay.md) (intermediate, 20 min): Send your server's mail (alerts, cron output, app email) through an SMTP relay on port 587 with Postfix, so it's delivered without running a full mail server.
- [How to set up a WireGuard VPN server on AlmaLinux and Rocky Linux](https://vpsnine.com/tutorials/wireguard-vpn-server-almalinux.md) (intermediate, 30 min): Install WireGuard on AlmaLinux or Rocky Linux, write wg0.conf, route clients through the server with firewalld masquerading, and connect your devices.
- [How to set up a WireGuard VPN server on Ubuntu 24.04](https://vpsnine.com/tutorials/wireguard-vpn-server-ubuntu.md) (intermediate, 30 min): Install WireGuard on Ubuntu 24.04, write the server config, route clients' traffic through it and connect a laptop and a phone.

## Web servers

- [How to host a static website with nginx on Ubuntu or Debian](https://vpsnine.com/tutorials/host-static-website-nginx.md) (beginner, 25 min): Serve a static site with nginx from your own server, with clean URLs, gzip, HTTP/2, the right caching headers and one-command deploys with rsync.
- [How to install Node.js on Ubuntu 24.04](https://vpsnine.com/tutorials/install-nodejs-ubuntu.md) (beginner, 10 min): Three ways to install Node.js on Ubuntu 24.04, NodeSource, nvm and Ubuntu's own package, which one to pick, and how to update or switch versions later.
- [How to point a domain at your VPS (A and AAAA records)](https://vpsnine.com/tutorials/point-a-domain-to-your-vps.md) (beginner, 15 min): Point your domain at your server with A and AAAA records, add www, pick a sensible TTL, and check with dig that the change has really taken effect.
- [Deploy a FastAPI app with Gunicorn, Uvicorn workers and systemd](https://vpsnine.com/tutorials/deploy-fastapi-gunicorn-nginx.md) (intermediate, 30 min): Run a FastAPI app in a virtualenv under Gunicorn with Uvicorn workers, keep it up with a systemd service, and put nginx in front as a reverse proxy.
- [Deploy a Node.js app with PM2 and nginx on Ubuntu 24.04](https://vpsnine.com/tutorials/deploy-nodejs-app-pm2-nginx-ubuntu.md) (intermediate, 30 min): Run a Node.js app under its own user with PM2, bring it back after reboots, put nginx in front as a reverse proxy, and ship updates without downtime.
- [Deploy on push with GitHub Actions and the VPSNine API](https://vpsnine.com/tutorials/deploy-on-push-github-actions-vpsnine-api.md) (intermediate, 40 min): Build on every push to main in GitHub Actions, snapshot the VPSNine server through the API, then ship with rsync over SSH and switch releases atomically.
- [How to get a Let's Encrypt wildcard certificate with the DNS challenge](https://vpsnine.com/tutorials/letsencrypt-wildcard-certificate-dns-challenge.md) (intermediate, 25 min): Get a free *.example.com certificate from Let's Encrypt with certbot or acme.sh and your DNS provider's API, renew it automatically and reload your web server.
- [How to use Caddy as a reverse proxy for multiple sites and apps](https://vpsnine.com/tutorials/caddy-reverse-proxy-multiple-apps.md) (intermediate, 30 min): One Caddyfile that serves several domains and apps on one server, each with its own automatic HTTPS certificate, path routing, headers, logs and basic auth.
- [Install PHP 8.3 with PHP-FPM and nginx on Ubuntu 24.04](https://vpsnine.com/tutorials/install-php-fpm-nginx-ubuntu.md) (intermediate, 25 min): Install PHP 8.3 and PHP-FPM from Ubuntu's archive, wire it to nginx, give each site its own FPM pool and user, and tune the settings that matter.
- [Receive VPSNine webhooks in Node.js and Python](https://vpsnine.com/tutorials/receive-vpsnine-webhooks-in-nodejs-and-python.md) (intermediate, 35 min): A dependency-free webhook receiver in Node.js or Python that checks every VPSNine signature, refuses replays, skips duplicates and runs under systemd.
- [Run a multi-container app with Docker Compose and a reverse proxy](https://vpsnine.com/tutorials/docker-compose-reverse-proxy.md) (intermediate, 30 min): One compose.yaml with an app, its PostgreSQL database and Caddy for HTTPS, on private networks with named volumes, health checks, secrets, updates and backups.
- [WP-CLI tutorial: manage and harden WordPress from the command line](https://vpsnine.com/tutorials/wp-cli-manage-wordpress.md) (intermediate, 35 min): Install WP-CLI, then update WordPress, manage plugins and users, move a site to a new domain, back up the database and lock the site down, all over SSH.
- [Create and destroy preview servers per pull request](https://vpsnine.com/tutorials/preview-servers-per-pull-request.md) (advanced, 45 min): Order a VPSNine server per pull request from GitHub Actions, deploy the branch, comment its address, and delete the server when the PR closes.

## Databases

- [How to install and secure MariaDB on Ubuntu 24.04](https://vpsnine.com/tutorials/install-mariadb-ubuntu.md) (beginner, 15 min): Install MariaDB on Ubuntu 24.04, answer mariadb-secure-installation correctly, give each app its own user and keep port 3306 private.
- [How to install PostgreSQL on Ubuntu 24.04 and keep it private](https://vpsnine.com/tutorials/install-postgresql-ubuntu.md) (beginner, 20 min): Install PostgreSQL 16 from Ubuntu or 18 from the PostgreSQL repository, create a role and a database, and connect safely over an SSH tunnel.
- [How to install and secure Redis on Ubuntu 24.04](https://vpsnine.com/tutorials/install-redis-ubuntu.md) (intermediate, 20 min): Install Redis on Ubuntu 24.04, keep it on localhost, replace the open default user with ACL users, block dangerous commands and cap its memory.

## Containers

- [Self-host Uptime Kuma with Docker and HTTPS](https://vpsnine.com/tutorials/self-host-uptime-kuma.md) (beginner, 20 min): Run Uptime Kuma 2 in Docker Compose, claim it privately over an SSH tunnel, put Caddy in front for HTTPS, then add monitors, alerts and a status page.
- [Run a multi-container app with Docker Compose and a reverse proxy](https://vpsnine.com/tutorials/docker-compose-reverse-proxy.md) (intermediate, 30 min): One compose.yaml with an app, its PostgreSQL database and Caddy for HTTPS, on private networks with named volumes, health checks, secrets, updates and backups.
- [Self-host Gitea with Docker, PostgreSQL and HTTPS](https://vpsnine.com/tutorials/self-host-gitea.md) (intermediate, 30 min): Run your own Git server with Gitea and PostgreSQL in Docker Compose, Git over SSH on port 2222, HTTPS from Caddy, no open sign-ups, and backups you can restore.
- [Self-host Plausible Analytics (Community Edition) with Docker](https://vpsnine.com/tutorials/self-host-plausible-analytics.md) (intermediate, 30 min): Install Plausible Community Edition with its official Docker Compose setup, serve it over HTTPS with Caddy, add your site, and back up and upgrade it.
- [Self-host Vaultwarden safely with Docker and HTTPS](https://vpsnine.com/tutorials/self-host-vaultwarden.md) (intermediate, 30 min): Run Vaultwarden, the Bitwarden-compatible password server, in Docker behind Caddy with HTTPS, sign-ups closed, a hashed admin token and backups you can restore.

## Monitoring

- [How to monitor a Linux server with Netdata](https://vpsnine.com/tutorials/install-netdata.md) (beginner, 15 min): Install Netdata for per-second CPU, memory, disk and network charts, keep its dashboard private behind an SSH tunnel, and get its built-in alerts.
- [How to read logs with journalctl](https://vpsnine.com/tutorials/read-logs-with-journalctl.md) (beginner, 15 min): Find the log lines you need with journalctl, by service, time, priority and boot, keep logs across reboots, and cap how much disk the journal uses.
- [Self-host Uptime Kuma with Docker and HTTPS](https://vpsnine.com/tutorials/self-host-uptime-kuma.md) (beginner, 20 min): Run Uptime Kuma 2 in Docker Compose, claim it privately over an SSH tunnel, put Caddy in front for HTTPS, then add monitors, alerts and a status page.
- [Get Slack and Discord alerts from VPSNine webhooks](https://vpsnine.com/tutorials/slack-and-discord-alerts-from-vpsnine-webhooks.md) (intermediate, 25 min): VPSNine has no Slack or Discord integration, so run a tiny relay that verifies webhook signatures and posts alerts and events to your channels.
- [How to troubleshoot high CPU and memory usage on a Linux server](https://vpsnine.com/tutorials/troubleshoot-high-cpu-and-memory.md) (intermediate, 20 min): Find the process behind high CPU or memory use with top, ps, vmstat and pidstat, read load average and steal time correctly, and spot the OOM killer.
- [Self-host Plausible Analytics (Community Edition) with Docker](https://vpsnine.com/tutorials/self-host-plausible-analytics.md) (intermediate, 30 min): Install Plausible Community Edition with its official Docker Compose setup, serve it over HTTPS with Caddy, add your site, and back up and upgrade it.

## Backups

- [Automate snapshots before risky changes with the CLI](https://vpsnine.com/tutorials/automate-snapshots-before-risky-changes-cli.md) (beginner, 15 min): A small script that takes a VPSNine snapshot with the vps9 CLI, waits until it is ready and rotates old ones, then runs your risky change.
- [How to automate BorgBackup with borgmatic to a second server](https://vpsnine.com/tutorials/borgmatic-backup-setup.md) (intermediate, 40 min): Back up files and databases to a second server over SSH with borgmatic: an append-only key, one YAML config, retention, checks and a daily timer.
- [How to back up a Linux server with restic to S3 storage](https://vpsnine.com/tutorials/restic-backup-s3.md) (intermediate, 30 min): Encrypted nightly backups to any S3-compatible bucket with restic: database dumps, a systemd timer, a retention policy and a tested restore.

## Self-hosted apps

- [Self-host Uptime Kuma with Docker and HTTPS](https://vpsnine.com/tutorials/self-host-uptime-kuma.md) (beginner, 20 min): Run Uptime Kuma 2 in Docker Compose, claim it privately over an SSH tunnel, put Caddy in front for HTTPS, then add monitors, alerts and a status page.
- [How to use Caddy as a reverse proxy for multiple sites and apps](https://vpsnine.com/tutorials/caddy-reverse-proxy-multiple-apps.md) (intermediate, 30 min): One Caddyfile that serves several domains and apps on one server, each with its own automatic HTTPS certificate, path routing, headers, logs and basic auth.
- [Self-host Gitea with Docker, PostgreSQL and HTTPS](https://vpsnine.com/tutorials/self-host-gitea.md) (intermediate, 30 min): Run your own Git server with Gitea and PostgreSQL in Docker Compose, Git over SSH on port 2222, HTTPS from Caddy, no open sign-ups, and backups you can restore.
- [Self-host Plausible Analytics (Community Edition) with Docker](https://vpsnine.com/tutorials/self-host-plausible-analytics.md) (intermediate, 30 min): Install Plausible Community Edition with its official Docker Compose setup, serve it over HTTPS with Caddy, add your site, and back up and upgrade it.
- [Self-host Vaultwarden safely with Docker and HTTPS](https://vpsnine.com/tutorials/self-host-vaultwarden.md) (intermediate, 30 min): Run Vaultwarden, the Bitwarden-compatible password server, in Docker behind Caddy with HTTPS, sign-ups closed, a hashed admin token and backups you can restore.
- [WP-CLI tutorial: manage and harden WordPress from the command line](https://vpsnine.com/tutorials/wp-cli-manage-wordpress.md) (intermediate, 35 min): Install WP-CLI, then update WordPress, manage plugins and users, move a site to a new domain, back up the database and lock the site down, all over SSH.

## Development

- [Automate snapshots before risky changes with the CLI](https://vpsnine.com/tutorials/automate-snapshots-before-risky-changes-cli.md) (beginner, 15 min): A small script that takes a VPSNine snapshot with the vps9 CLI, waits until it is ready and rotates old ones, then runs your risky change.
- [How to install Node.js on Ubuntu 24.04](https://vpsnine.com/tutorials/install-nodejs-ubuntu.md) (beginner, 10 min): Three ways to install Node.js on Ubuntu 24.04, NodeSource, nvm and Ubuntu's own package, which one to pick, and how to update or switch versions later.
- [Use curl and jq with the VPSNine API](https://vpsnine.com/tutorials/use-curl-and-jq-with-the-vpsnine-api.md) (beginner, 20 min): Drive the VPSNine API from a shell with curl and jq: filter servers, take snapshots and wait for them, read errors, page invoices, edit the firewall.
- [Deploy a FastAPI app with Gunicorn, Uvicorn workers and systemd](https://vpsnine.com/tutorials/deploy-fastapi-gunicorn-nginx.md) (intermediate, 30 min): Run a FastAPI app in a virtualenv under Gunicorn with Uvicorn workers, keep it up with a systemd service, and put nginx in front as a reverse proxy.
- [Deploy a Node.js app with PM2 and nginx on Ubuntu 24.04](https://vpsnine.com/tutorials/deploy-nodejs-app-pm2-nginx-ubuntu.md) (intermediate, 30 min): Run a Node.js app under its own user with PM2, bring it back after reboots, put nginx in front as a reverse proxy, and ship updates without downtime.
- [Deploy on push with GitHub Actions and the VPSNine API](https://vpsnine.com/tutorials/deploy-on-push-github-actions-vpsnine-api.md) (intermediate, 40 min): Build on every push to main in GitHub Actions, snapshot the VPSNine server through the API, then ship with rsync over SSH and switch releases atomically.
- [Get Slack and Discord alerts from VPSNine webhooks](https://vpsnine.com/tutorials/slack-and-discord-alerts-from-vpsnine-webhooks.md) (intermediate, 25 min): VPSNine has no Slack or Discord integration, so run a tiny relay that verifies webhook signatures and posts alerts and events to your channels.
- [How to write a systemd service for your app](https://vpsnine.com/tutorials/create-a-systemd-service.md) (intermediate, 20 min): Run your own program as a systemd service that starts at boot, restarts when it crashes, runs as its own user, reads an env file and is sandboxed.
- [Manage VPSNine servers with Ansible](https://vpsnine.com/tutorials/manage-vpsnine-servers-with-ansible.md) (intermediate, 35 min): Build an Ansible inventory from the VPSNine API, grouped by tag, plan and image, and run a playbook that patches servers and locks SSH to keys.
- [Receive VPSNine webhooks in Node.js and Python](https://vpsnine.com/tutorials/receive-vpsnine-webhooks-in-nodejs-and-python.md) (intermediate, 35 min): A dependency-free webhook receiver in Node.js or Python that checks every VPSNine signature, refuses replays, skips duplicates and runs under systemd.
- [Script your infrastructure with the TypeScript SDK](https://vpsnine.com/tutorials/script-vpsnine-with-the-typescript-sdk.md) (intermediate, 30 min): Use @vpsnine/sdk from TypeScript to list servers, snapshot before a reboot, order a server safely and handle errors, with retries and idempotency keys built in.
- [Self-host Gitea with Docker, PostgreSQL and HTTPS](https://vpsnine.com/tutorials/self-host-gitea.md) (intermediate, 30 min): Run your own Git server with Gitea and PostgreSQL in Docker Compose, Git over SSH on port 2222, HTTPS from Caddy, no open sign-ups, and backups you can restore.
- [Create and destroy preview servers per pull request](https://vpsnine.com/tutorials/preview-servers-per-pull-request.md) (advanced, 45 min): Order a VPSNine server per pull request from GitHub Actions, deploy the branch, comment its address, and delete the server when the PR closes.
