What a “VPS” actually means (in one minute)
A <strong>VPS</strong> (Virtual Private Server) is normally a rented slice of a data-center machine that:
- Stays <strong>on 24/7</strong>
- Has a <strong>public IP address</strong> (or a way to reach it)
- Runs your apps in the background, even when your own computer is off
Important: an old laptop can do <strong>all of this for free</strong>, with one trade-off — Someone else's data center gives you power backup, cooling, and fast networking automatically.
- With a laptop, <strong>you</strong> become responsible for uptime, power, and networking
If you remember one rule: > <strong>A VPS is just "a computer that's always on and reachable." Your laptop can be that computer.</strong>
The 3 building blocks you'll see everywhere
1) The OS layer
The operating system running on the laptop, stripped down to run headless (no GUI needed). Common beginners' choices:
- <strong>Ubuntu Server</strong>: huge community, most tutorials assume this
- <strong>Debian</strong>: lighter, very stable, fewer built-in conveniences
- <strong>Fedora Server</strong>: newer packages, smaller community
2) The networking layer
How the outside world (or your team) actually reaches the laptop.
- <strong>Port forwarding</strong>: router sends traffic directly to the laptop
- <strong>Tunnel service</strong>: a third party (Cloudflare, Tailscale) creates a path to your laptop without opening your router
3) The hosting layer
What actually runs your apps and keeps them alive.
- <strong>Docker / Docker Compose</strong>: packages and isolates each service
- <strong>systemd</strong>: makes sure things restart on crash or reboot
- <strong>Reverse proxy</strong> (Caddy/Nginx): routes traffic + handles HTTPS
Core self-hosting mental model (beginner edition)
Your laptop-as-VPS has these "responsibilities." Setup should help you answer:
- <strong>Does it stay on?</strong> (power, sleep settings, auto-restart after outage)
- <strong>Can people reach it?</strong> (networking, DNS, firewall)
- <strong>Does it run my apps reliably?</strong> (process management, containers)
- <strong>Is it safe to expose to the internet?</strong> (firewall, SSH hardening, updates)
Hardware/power setup helps with (1), networking helps with (2), Docker/systemd help with (3), and security practices help with (4).
What should you prep before installing anything?
Start with the physical machine. Beginners often jump to software and get burned by power/hardware issues later.
Hardware-level (the "must have" set)
For the laptop itself:
- <strong>Keep it plugged in</strong> permanently — the battery becomes a free UPS during power blips
- <strong>Disable sleep/hibernate</strong> completely
- <strong>Set "lid close = do nothing"</strong> so it doesn't sleep when you shut the lid
- <strong>Enable "power on after power loss"</strong> in BIOS if available
OS-level (foundation health)
- Fresh install of a <strong>server</strong> OS (no desktop environment — saves RAM/CPU)
- <strong>Static local IP</strong> or DHCP reservation, so the laptop's address never changes
- <strong>SSH enabled</strong> at install time, so you never need a monitor/keyboard again after setup
Network-level (where reachability lives)
- Router admin access (for port forwarding), or
- A tunnel account (Cloudflare/Tailscale) if you don't have router access or are behind CGNAT
- A domain name (optional, but makes HTTPS and memorable URLs possible)
Setting up the OS: step-by-step
1) Flash and install
- Download <strong>Ubuntu Server LTS</strong> (most beginner-friendly, long-term support)
- Flash it to a USB drive with <strong>Rufus</strong> (Windows) or <strong>balenaEtcher</strong> (Mac/Linux)
- Boot the laptop from USB, follow the installer, and <strong>tick "Install OpenSSH server"</strong> when prompted
2) First login and updates
ssh youruser@laptop-local-ip
sudo apt update && sudo apt upgrade -y3) Stop it from sleeping
sudo nano /etc/systemd/logind.confSet these values:
HandleLidSwitch=ignore
HandleLidSwitchDocked=ignore
HandleSuspendKey=ignoresudo systemctl restart systemd-logind4) Give it a static IP
Easiest option: log into your router and set a <strong>DHCP reservation</strong> for the laptop's MAC address, so it always gets the same local IP (e.g. <code>192.168.1.50</code>).
Networking: the part beginners get stuck on
Option A: Port forwarding (traditional, free, but has caveats)
- Log into your router's admin panel
- Forward external ports (80, 443, and any custom app ports) to the laptop's local IP
- Get a <strong>free dynamic DNS</strong> name so people can reach you even when your home IP changes:
- <strong>DuckDNS</strong> (simplest, free forever)
- <strong>No-IP</strong> (free with periodic re-confirmation)
- <strong>Check for CGNAT first</strong>: compare your router's WAN IP to <code>whatismyip.com</code>. If they differ, your ISP is blocking direct inbound traffic and port forwarding <strong>won't work</strong> — skip to Option B.
Option B: Tunnel service (easier, more secure, works behind CGNAT)
This is the recommended default for most beginners in 2026.
- <strong>Cloudflare Tunnel</strong> (100% free):
- No port forwarding, no exposed home IP, automatic free HTTPS
- Works even behind CGNAT or strict ISPs
- Setup:
curl -L https://pkg.cloudflare.com/cloudflare-main.gpg | sudo gpg --dearmor -o /usr/share/keyrings/cloudflare-main.gpg
echo "deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update && sudo apt install cloudflared -y
cloudflared tunnel login
cloudflared tunnel create my-vps
cloudflared tunnel route dns my-vps yourdomain.com- Point the tunnel config at your local service (e.g. <code>localhost:8080</code>) and start it as a systemd service so it survives reboots.
- <strong>Tailscale</strong> (free for personal use):
- Creates a <strong>private mesh network</strong> between your devices
- Great if you (or your team) just need private access, not public hosting
- Add <strong>Tailscale Funnel</strong> on top if you want to expose a service publicly through it too
Beginner tip:
- Use <strong>Cloudflare Tunnel</strong> for anything public-facing.
- Use <strong>Tailscale</strong> for private admin access (SSH, dashboards) so you're not exposing SSH to the whole internet.
Hosting your apps: how to run services beginners can actually manage
Process types that are most useful early
- <strong>Docker containers</strong>: isolated, reproducible, easy to update/rollback
- <strong>systemd services</strong>: simplest for a single app (like one JAR file)
- <strong>Docker Compose</strong>: best once you have 2+ services (app + database + cache)
Install Docker
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker $USER
newgrp docker
docker --versionA minimal Docker Compose setup
services:
backend:
build: .
ports:
- "8080:8080"
restart: always
environment:
- SPRING_PROFILES_ACTIVE=prod
db:
image: postgres:16
restart: always
environment:
- POSTGRES_DB=yourdb
- POSTGRES_PASSWORD=yourpassword
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:docker compose up -d --build
docker compose logs -fBeginner tip:
- Create <strong>one Compose file per project</strong>, not one giant file for everything.
- Add <code>restart: always</code> to every service so they come back after a reboot or crash.
Reverse proxy and HTTPS: the rules of thumb that prevent broken sites
1) Never expose app ports directly
Instead of pointing your domain at:
- <code>yourdomain.com:8080</code>
Put a reverse proxy in front so visitors just hit:
- <code>yourdomain.com</code> (port 443, HTTPS)
2) Use Caddy for near-zero-config HTTPS
sudo apt install caddy -y
sudo nano /etc/caddy/Caddyfileyourdomain.com {
reverse_proxy localhost:8080
}sudo systemctl restart caddyCaddy automatically requests and renews a free <strong>Let's Encrypt</strong> certificate — no manual TLS setup needed.
3) If using Cloudflare Tunnel, skip local HTTPS
Cloudflare terminates HTTPS for you at the edge — the tunnel connects to your app over plain <code>localhost:8080</code>, so you don't need Caddy just for certificates (though it's still useful for routing multiple apps).
Security: non-negotiable basics for anything internet-facing
1) Lock down the firewall
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enableOnly open what you actually use.
2) Harden SSH
- Disable password login, use SSH key pairs only
- Consider moving SSH off port 22 to cut down on bot noise
- Or better: only allow SSH over <strong>Tailscale</strong>, and block port 22 from the public internet entirely
3) Add fail2ban
sudo apt install fail2ban -yBlocks IPs that repeatedly fail login attempts.
4) Keep the system patched
sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure unattended-upgrades5) Use cardinality of access responsibly
If multiple people need access:
- Give each person their <strong>own</strong> SSH key and user account
- Avoid sharing one root login among a team
Beginner rule:
- Expose only ports 80/443 (or nothing at all if using a tunnel) — everything else should be reachable only through Tailscale or SSH keys.
Common laptop-as-VPS beginner pitfalls
Pitfall A: Assuming your ISP allows inbound traffic
Many home ISPs use CGNAT, silently blocking port forwarding. Fix:
- Check WAN IP vs public IP before troubleshooting router settings for hours.
- Use Cloudflare Tunnel or Tailscale Funnel instead.
Pitfall B: "It works" ≠ "It stays working"
A laptop can sleep, lose power, or overheat when you're not looking. Fix:
- Disable sleep, keep it plugged in, add <code>restart: always</code> to every container.
Pitfall C: Too many exposed ports
Opening every port "just in case" invites scanning and attacks. Fix:
- Only forward/allow 80, 443, and SSH (ideally SSH only over a private tunnel).
Pitfall D: No monitoring
A silent crash at 2 AM can go unnoticed for days. Fix:
- Self-host <strong>Uptime Kuma</strong> (free, runs in Docker) to alert you when a service goes down.
A practical starter checklist (Day 1 → Day 7)
Day 1: Prep hardware + install OS
- Disable sleep, keep plugged in
- Install Ubuntu Server, enable OpenSSH
- Set static local IP
Day 2: Set up remote access
- Check for CGNAT
- Install Cloudflare Tunnel or set up port forwarding + DDNS
Day 3: Install Docker
- Install Docker + Docker Compose
- Test with a simple <code>hello-world</code> container
Day 4: Deploy your first service
- Build and run your backend via Docker Compose
- Confirm it's reachable locally (<code>curl localhost:8080</code>)
Day 5: Add reverse proxy + HTTPS
- Install Caddy (or configure the tunnel to route your domain)
- Confirm <code>https://yourdomain.com</code> loads your app
Day 6: Lock down security
- Configure <code>ufw</code>, disable SSH password auth, install <code>fail2ban</code>
- Set up <code>unattended-upgrades</code>
Day 7: Add monitoring
- Deploy Uptime Kuma
- Set up an alert (email/Discord/Telegram) for downtime
How to name things so your future self doesn't suffer
Use consistent names:
- Machines: <code>home-server</code>, <code>backend-vps</code>
- Compose projects: one folder per project (<code>~/projects/orders-api/</code>)
- Domains/subdomains: <code>api.yourdomain.com</code>, <code>status.yourdomain.com</code>
Services:
- Name systemd units and containers after the app, not generic names like <code>app1</code>
- Example: <code>orders-backend</code>, <code>orders-db</code>
Minimal example: what a full stack should look like
A good self-hosted setup has:
- <strong>OS</strong>: Ubuntu Server, sleep disabled, static IP
- <strong>Access</strong>: Cloudflare Tunnel (public) + Tailscale (private admin)
- <strong>Runtime</strong>: Docker Compose with <code>restart: always</code>
- <strong>Routing</strong>: Caddy or Cloudflare-terminated HTTPS
- <strong>Security</strong>: <code>ufw</code>, SSH keys only, <code>fail2ban</code>, auto-updates
- <strong>Monitoring</strong>: Uptime Kuma with alerts
Beginner setup summary:
- "Ubuntu Server on an old laptop, reachable via Cloudflare Tunnel at <code>api.yourdomain.com</code>, running the backend in Docker with Postgres, monitored by Uptime Kuma."
Final takeaway
An old laptop is where you turn spare hardware into a real, always-on backend host — for free. If you start with:
- hardware that stays awake and powered
- a reachable network path (tunnel or port forward)
- containerized services that auto-restart
- basic security locked down
- one monitoring dashboard
…you will already have a production-grade home VPS, not just "a laptop running something."