VPS Project Specs: Privacy and Security

13 Coding Agent-Ready Specs | 1 CPU / 1 GB RAM / 20 GB Disk VPS
Part 1 of 3

🛡 WireGuard VPN

RAM: ~5 MB | Tech: C / Kernel
5 MB RAMKernelMust-Have

The fastest, leanest VPN protocol in existence. Kernel-level performance, handles hundreds of Mbps on a single core.

Architecture

+--------+     UDP/51820     +--------+
| Client |<================>|  VPS   |
| Phone  |  Encrypted Tunnel |WireGrd |
| Laptop |                   +--------+
+--------+                      |
                           +----+----+
                           |10.0.0.0 |
                           | Subnet  |

Docker Compose

services:
  wireguard:
    image: linuxserver/wireguard
    container_name: wireguard
    cap_add: [NET_ADMIN, SYS_MODULE]
    environment:
      PUID: 1000
      PGID: 1000
      TZ: UTC
      SERVERURL: auto
      SERVERPORT: 51820
      PEERS: phone,laptop,desktop
      PEERDNS: auto
      INTERNAL_SUBNET: 10.13.13.0
    volumes:
      - ./config:/config
      - /lib/modules:/lib/modules
    ports:
      - 51820:51820/udp
    sysctls:
      net.ipv4.conf.all.src_valid_mark: 1
    restart: unless-stopped

Caddy Reverse Proxy

# Not proxied (UDP protocol). Serve config download page:
wireguard.example.com {
  reverse_proxy localhost:51821
}

Security Hardening

Backup Strategy

Backup /config dir: tar czf wg-backup.tar.gz ./config

Health Check

wg show

🔗 Headscale

RAM: ~50 MB | Tech: Go
50 MB RAMGoDocker

Self-hosted Tailscale control server. Zero-config mesh VPN, works behind NAT, handles key exchange, ACLs, and DNS.

Architecture

+--------+   DERP/HTTPS   +----------+   WireGuard   +--------+
| Device |<===============>| Headscale|<=============>| Device |
| Phone  |                 | Control  |               | Laptop |
+--------+                 | Server   |               +--------+
                           +----------+

Docker Compose

services:
  headscale:
    image: headscale/headscale:latest
    container_name: headscale
    volumes:
      - ./config:/etc/headscale
      - ./data:/var/lib/headscale
    ports:
      - 8080:8080
    command: headscale serve
    restart: unless-stopped
  headscale-ui:
    image: ghcr.io/gurucomputing/headscale-ui:latest
    container_name: headscale-ui
    ports:
      - 8443:80
    restart: unless-stopped

Caddy Reverse Proxy

headscale.example.com {
  reverse_proxy headscale:8080
}
headscale-ui.example.com {
  reverse_proxy headscale-ui:80
}

Security Hardening

Backup Strategy

Backup config/ and data/ directories (includes SQLite DB)

Health Check

docker exec headscale headscale nodes list

🌐 Cloudflared

RAM: ~10 MB | Tech: Go
10 MB RAMGoEasy

Expose local services without opening ports. Free Cloudflare proxy with DDoS protection included.

Architecture

Internet --> Cloudflare Edge --> Tunnel --> VPS Services
                                    |
                         +----------+----------+
                         |     cloudflared     |
                         |  :8080 :3000 :5000  |

Docker Compose

services:
  cloudflared:
    image: cloudflare/cloudflared:latest
    container_name: cloudflared
    command: tunnel --no-autoupdate run
    environment:
      TUNNEL_TOKEN: your-token-here
    restart: unless-stopped

Caddy Reverse Proxy

# Not needed - Cloudflare IS the reverse proxy
# Configure in Cloudflare dashboard

Security Hardening

Backup Strategy

Token stored in Cloudflare dashboard. Stateless via token.

Health Check

docker logs cloudflared --tail 5

🕳 Pi-hole

RAM: ~50 MB | Tech: PHP / Shell
50 MB RAMPHPDockerMust-Have

Network-wide DNS ad blocker. Block ads, trackers, and malware for every device. Beautiful web dashboard.

Architecture

Devices --> Pi-hole DNS (:53) --> Upstream DNS
    |           |
    |     Block List
    |     Check
    v           v
  Allowed    Blocked
  (resolve)  (0.0.0.0)

Docker Compose

services:
  pihole:
    image: pihole/pihole:latest
    container_name: pihole
    environment:
      TZ: UTC
      WEBPASSWORD: changeme
      DNS1: 127.0.0.1#5335
      DNS2: "no"
    volumes:
      - ./etc-pihole:/etc/pihole
      - ./etc-dnsmasq.d:/etc/dnsmasq.d
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "8080:80"
    cap_add: [NET_ADMIN]
    restart: unless-stopped

Caddy Reverse Proxy

pihole.example.com {
  reverse_proxy pihole:80
}

Security Hardening

Backup Strategy

Backup etc-pihole/ and etc-dnsmasq.d/ directories

Health Check

dig @localhost example.com +short

🔒 AdGuard Home

RAM: ~40 MB | Tech: Go
40 MB RAMGoOne Binary

Pi-hole alternative with slicker UI, built-in HTTPS, and DNS-over-HTTPS/TLS support. Modern ad-blocking at DNS level.

Architecture

Devices --> AdGuard DNS (:53/:853) --> Upstream DoH/DoT
              |
         Block Lists
         + Safe Browsing
         + Parental Ctrl

Docker Compose

services:
  adguard:
    image: adguard/adguardhome:latest
    container_name: adguard
    volumes:
      - ./work:/opt/adguardhome/work
      - ./conf:/opt/adguardhome/conf
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "853:853/tcp"
      - "3000:3000"
      - "8080:80"
    restart: unless-stopped

Caddy Reverse Proxy

adguard.example.com {
  reverse_proxy adguard:80
}

Security Hardening

Backup Strategy

Backup work/ and conf/ directories

Health Check

curl -s localhost:3000/control/status

🔍 Unbound DNS Resolver

RAM: ~20 MB | Tech: C
20 MB RAMCEasy

Validating, recursive, caching DNS resolver with DNSSEC. Run your own DNS instead of trusting Google/Cloudflare.

Architecture

Pi-hole --> Unbound (:5335) --> Root DNS Servers
              |
         Recursive
         Resolution
         + DNSSEC
         Validation

Docker Compose

services:
  unbound:
    image: mvance/unbound:latest
    container_name: unbound
    volumes:
      - ./unbound.conf:/opt/unbound/etc/unbound/unbound.conf
    ports:
      - "5335:5335/tcp"
      - "5335:5335/udp"
    restart: unless-stopped

Caddy Reverse Proxy

# Internal DNS only - not exposed to internet

Security Hardening

Backup Strategy

Backup unbound.conf. Config is minimal.

Health Check

dig @localhost -p 5335 example.com +dnssec +short

🏰 Vaultwarden

RAM: ~15 MB | Tech: Rust
15 MB RAMRustDockerMust-Have

Self-hosted Bitwarden-compatible password manager. Single Rust binary, works with all official Bitwarden apps and extensions.

Architecture

+--------+    HTTPS    +-----------+     +-------+
|Browser |<===========>|Vaultwarden|<===>| SQLite |
|App/CLI |             |  :8080    |     |  DB   |
+--------+             +-----------+     +-------+

Docker Compose

services:
  vaultwarden:
    image: vaultwarden/server:latest
    container_name: vaultwarden
    environment:
      DOMAIN: https://vw.example.com
      SIGNUPS_ALLOWED: "false"
      WEBSOCKET_ENABLED: "true"
      ADMIN_TOKEN: your-admin-token
    volumes:
      - ./vw-data:/data
    ports:
      - "8080:80"
    restart: unless-stopped

Caddy Reverse Proxy

vw.example.com {
  reverse_proxy vaultwarden:80
}

Security Hardening

Backup Strategy

Backup vw-data/ dir (SQLite DB + attachments). CRITICAL.

Health Check

curl -s http://localhost:8080/alive

🔔 ntfy

RAM: ~20 MB | Tech: Go
20 MB RAMGoDocker

Simple HTTP-based push notification service. Send alerts to phone/desktop from scripts, cron jobs, or any HTTP request.

Architecture

Script/Cron --> ntfy (:80) --> Phone App
                      |
                 WebSocket
                 + WebPush
                 + Firebase

Docker Compose

services:
  ntfy:
    image: binwiederhier/ntfy:latest
    container_name: ntfy
    command: serve
    environment:
      NTFY_BASE_URL: https://ntfy.example.com
      NTFY_CACHE_FILE: /var/cache/ntfy/cache.db
      NTFY_AUTH_FILE: /var/lib/ntfy/user.db
      NTFY_AUTH_DEFAULT_ACCESS: deny-all
    volumes:
      - ./ntfy-cache:/var/cache/ntfy
      - ./ntfy-data:/var/lib/ntfy
    ports:
      - "80:80"
    restart: unless-stopped

Caddy Reverse Proxy

ntfy.example.com {
  reverse_proxy ntfy:80
}

Security Hardening

Backup Strategy

Backup ntfy-data/ for user accounts

Health Check

curl -s http://localhost:80/v1/health

💣 One-Time Secret

RAM: ~30 MB | Tech: Ruby
30 MB RAMRubyDocker

Share sensitive info via self-destructing links. Secret encrypted in-browser, stored temporarily, destroyed after viewing.

Architecture

Sender --> OTS (:7143) --> Secret Link
                              |
                         View Once
                         Then Destroy

Docker Compose

services:
  ots:
    image: onetimesecret/ots:latest
    container_name: ots
    ports:
      - "7143:443"
    environment:
      APP_HOST: ots.example.com
      SSL_ENABLED: "false"
    restart: unless-stopped

Caddy Reverse Proxy

ots.example.com {
  reverse_proxy ots:443 { transport http { tls_insecure_skip_verify } }
}

Security Hardening

Backup Strategy

Stateless - secrets destroyed after viewing. No persistent data.

Health Check

curl -sk https://localhost:7143/

🕵 Tor Relay

RAM: ~30 MB | Tech: C
30 MB RAMCEasy

Run a Tor relay to help the Tor network. Non-exit relay uses minimal resources and contributes to internet freedom.

Architecture

Tor Network <--> Your Relay (:9001) <--> Tor Network
                      |
                 ORPort 9001
                 DirPort 9030

Docker Compose

services:
  tor-relay:
    image: osminogin/tor-simple:latest
    container_name: tor-relay
    volumes:
      - ./torrc:/etc/tor/torrc:ro
      - ./tor-data:/var/lib/tor
    ports:
      - "9001:9001"
      - "9030:9030"
    restart: unless-stopped

Caddy Reverse Proxy

# Not proxied - Tor uses its own network

Security Hardening

Backup Strategy

Backup tor-data/ for relay keys (preserves reputation)

Health Check

curl --socks5-hostname localhost:9050 https://check.torproject.org

📖 Wallabag

RAM: ~80 MB | Tech: PHP
80 MB RAMPHPDocker

Save web articles to read later, stripped of ads. Self-hosted Pocket alternative with full-text search, tags, ePUB/PDF export.

Architecture

+--------+    HTTPS    +---------+     +--------+
|Browser |<===========>| Wallabag|<===>|MariaDB |
|  App   |             |  :8080  |     | + Redis|
+--------+             +---------+     +--------+

Docker Compose

services:
  wallabag:
    image: wallabag/wallabag:latest
    container_name: wallabag
    environment:
      SYMFONY__ENV__DATABASE_DRIVER: pdo_mysql
      SYMFONY__ENV__DATABASE_HOST: wallabag-db
      SYMFONY__ENV__DATABASE_NAME: wallabag
      SYMFONY__ENV__DATABASE_USER: wallabag
      SYMFONY__ENV__DATABASE_PASSWORD: changeme
      SYMFONY__ENV__FOSUSER_REGISTRATION: "false"
    volumes:
      - ./wallabag-images:/var/www/wallabag/web/assets/images
    ports:
      - "8080:80"
    depends_on: [wallabag-db]
    restart: unless-stopped
  wallabag-db:
    image: mariadb:latest
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: wallabag
      MYSQL_USER: wallabag
      MYSQL_PASSWORD: changeme
    volumes:
      - ./wallabag-db:/var/lib/mysql
    restart: unless-stopped

Caddy Reverse Proxy

wallabag.example.com {
  reverse_proxy wallabag:80
}

Security Hardening

Backup Strategy

mysqldump wallabag > backup.sql. Also backup wallabag-images/

Health Check

curl -s http://localhost:8080/ | grep -q wallabag

🔖 Shaarli

RAM: ~15 MB | Tech: PHP
15 MB RAMPHPNo Database

Personal minimalist bookmarking platform. PHP-based, file storage (no database), incredibly light. Save links with tags and notes.

Architecture

+--------+    HTTPS    +--------+     +--------+
|Browser |<===========>|Shaarli |<===>|  JSON  |
|Ext.   |             |  :80   |     |  Files |
+--------+             +--------+     +--------+

Docker Compose

services:
  shaarli:
    image: shaarli/shaarli:latest
    container_name: shaarli
    volumes:
      - ./shaarli-data:/var/www/shaarli/data
    ports:
      - "8080:80"
    restart: unless-stopped

Caddy Reverse Proxy

shaarli.example.com {
  reverse_proxy shaarli:80
}

Security Hardening

Backup Strategy

Backup shaarli-data/ directory. File-based, simple tar/zip.

Health Check

curl -s http://localhost:80/ | grep -q Shaarli

📱 Gotify

RAM: ~25 MB | Tech: Go
25 MB RAMGoDocker

Self-hosted push notification server with Android app and CLI client. Send messages from scripts directly to your phone.

Architecture

Script/API --> Gotify (:8080) --> Android App
                    |
               WebSocket
               Push

Docker Compose

services:
  gotify:
    image: gotify/server:latest
    container_name: gotify
    volumes:
      - ./gotify-data:/app/data
    ports:
      - "8080:80"
    environment:
      GOTIFY_DEFAULTUSER_PASS: changeme
    restart: unless-stopped

Caddy Reverse Proxy

gotify.example.com {
  reverse_proxy gotify:80
}

Security Hardening

Backup Strategy

Backup gotify-data/ directory (messages and config)

Health Check

curl -s http://localhost:80/health
Starter Combo (~110 MB total)
WireGuard (5 MB) + Pi-hole+Unbound (70 MB) + Vaultwarden (15 MB) + ntfy (20 MB) = complete privacy fortress.
Generated by Bob | Back to Index | bob-first-page.pages.dev