Tutorials 12 min read

Self-Hosting Your Website in 2026: Complete Guide (VPS, Docker, HTTPS)

2026 technical guide to self-hosting on a VPS: choosing plans, Docker setup, Let's Encrypt HTTPS, security, and budget. Compare self-hosting vs. cloud.

S By Selfhostr Team · no sponsored content
Self-Hosting Your Website in 2026: Complete Guide (VPS, Docker, HTTPS)
ⓘ ⓘ This article may contain affiliate links (no extra cost to you, it funds the site). As an Amazon Associate, Selfhostr earns from qualifying purchases. See the disclosure.
💰
~€3 - €5/mo
Entry Cost
🧠
4 GB
Min RAM (WP)
💾
NVMe SSD
Storage Type
🌐
1-2 TB/mo
Bandwidth

👍 What we like

  • ✓Public fixed IP ensures reliable global accessibility
  • ✓High symmetric bandwidth improves site speed for visitors
  • ✓Guaranteed availability with provider SLA
  • ✓Remote reboot capability aids maintenance
  • ✓Cost-effective starting at ~€3/month for static sites

👎 What to watch

  • ✕Recurring monthly costs compared to one-time hardware
  • ✕Full security responsibility lies with the user
  • ✕Requires manual OS updates and firewall configuration
  • ✕ISP port blocking makes home hosting difficult
📑 Contents ▾

Hosting your own website in 2026 is no longer reserved for sysadmin experts or geeks with symmetric fiber connections. With the maturity of containerization tools, the democratization of free SSL certificates, and the drop in cloud infrastructure costs, the barrier to entry has decreased significantly. Yet, the temptation of the “click-and-go” SaaS platforms (Wix, Shopify, WordPress.com) remains strong.

This guide does not aim to convince you to become a full-time system administrator, but to provide a precise technical roadmap to regain control of your data, bandwidth, and long-term costs. We will deconstruct the process of installing a site (static, WordPress, or web application) on a VPS (Virtual Private Server), emphasizing robustness, security, and reproducibility via Docker.

Before diving into terminal commands, it is crucial to understand the arena you are entering. Physical self-hosting at home and VPS self-hosting are two distinct paradigms, with very different technical and financial trade-offs.

🖥️ Hardware to host it at home — To run this service 24/7 without a big power bill, a low-power mini PC is enough in most cases. Two common picks: the Beelink Mini S12 Pro (Intel N100, 16 GB) and the Beelink EQ14 (Intel N150, dual 2.5 GbE ports). To choose for your needs: our guide to the best mini PCs for a homelab.

Self-hosting vs VPS: The technical duel

The first decision to make is hardware-related. Do you have a PC running 24/7 in your living room, or will you rent a remote server?

Physical self-hosting (Home Lab)

This is the “old-school” option. You use an old laptop, a Raspberry Pi, or a mini-PC as a server.

  • Advantages: One-time hardware cost (often already amortized), no dependency on a cloud provider for immediate availability, total control over hardware.
  • Major drawbacks: Upload bandwidth is rarely symmetric (often 10-20 Mbps upload for a consumer connection), making the site slow for distant visitors. Furthermore, Internet Service Providers (ISPs) often block incoming ports 80 and 443, requiring complex tunneling solutions (Cloudflare Tunnel, Tailscale). Reliability depends on your electricity and connection.
  • Verdict: Ideal for internal projects, media (Jellyfin), or learning, but not recommended for a critical public site requiring high availability and low global latency.

VPS (Virtual Private Server)

A VPS is a dedicated virtual machine rented from a host (Hetzner, OVH, DigitalOcean, AWS Lightsail).

  • Advantages: Public fixed IP, high and symmetric bandwidth, guaranteed availability (SLA), remote reboot possible, data geographically close to your audience.
  • Drawbacks: Recurring monthly cost. Security responsibility (firewall, OS updates) rests entirely on you.
  • Verdict: The industrial standard for hosting a public site. It is the rational choice for performance, network simplicity, and professionalizing your infrastructure.

For the rest of this tutorial, we will assume you have opted for a VPS. It is the most reliable way to ensure your site stays online, fast, and secure without having to manage the noise of your fan at 3 AM.

Host this yourself

No hardware to manage? This kind of service runs on a managed VPS, ready in minutes. See PlanetHoster plans

Step 1: Choosing the right VPS offer

The VPS market in 2026 is saturated, but quality varies. Avoid generalist consumer hosts that sell “cloud” at rock-bottom prices but with poor disk I/O performance. Prefer hosts specialized in infrastructure.

Sizing and indicative costs

Here is a comparative analysis based on the average needs of a modern website in 2026.

CriterionEntry-level (Blog/Static)Mid-range (WordPress/DB)High Performance (App/API)
CPU1 vCPU (e.g., AMD EPYC)2 vCPU4+ vCPU
RAM1 GB - 2 GB4 GB8 GB+
Storage20 GB NVMe SSD50 GB NVMe SSD100 GB+ NVMe
Bandwidth1-2 TB/month2-5 TB/monthUnlimited or very high
Monthly Cost~€3 - €5~€8 - €15~€20 - €40
Host TypesHetzner Cloud, DigitalOceanHetzner, OVH, LinodeAWS, GCP, DigitalOcean

Technical Analysis: For a WordPress site with a local database, 1 GB of RAM is often too tight if you don’t do aggressive tuning. The MySQL/MariaDB engine consumes a lot of memory. 4 GB is the comfortable floor to avoid swap (which kills disk performance). For a static site (Hugo, Jekyll) or a lightweight Node.js app, 1 GB is more than enough.

Our recommendation: Start small. It is always possible to scale up on most modern cloud platforms without migrating your data. Choose a host that allows easy disk or snapshot migration.

Step 2: Initial server hardening

Once your VPS is provisioned, you receive an IP address and a root password via email. Do not connect directly with root via password for the rest of the process. The first thing to do is to harden SSH access.

  1. Create a dedicated user:

    adduser monuser
    usermod -aG sudo monuser
  2. Configure SSH keys: Generate an SSH key on your local machine (ssh-keygen) and copy it to the server (ssh-copy-id monuser@IP_OF_VPS).

  3. Disable root login and password login: Edit /etc/ssh/sshd_config:

    PermitRootLogin no
    PasswordAuthentication no

    Restart the SSH service (systemctl restart sshd).

  4. Configure the firewall (UFW): Enable the firewall and allow only necessary traffic.

    sudo ufw allow OpenSSH
    sudo ufw allow 80/tcp   # HTTP
    sudo ufw allow 443/tcp  # HTTPS
    sudo ufw enable

These steps eliminate 90% of automated attacks (brute force) that plague servers exposed to the public internet.

Step 3: Installing Docker and Docker Compose

Forget manual installation of Nginx, PHP, MySQL, or Node.js on the filesystem. In 2026, the standard is container isolation. Docker allows you to deploy your web stack in a reproducible, clean, and easy-to-update manner.

Installing Docker

The safest method is to use the official installation script or the packages from the Docker repository. The commands below are for Ubuntu; on Debian, replace ubuntu with debian in both URLs.

# System update
sudo apt update && sudo apt upgrade -y

# Install dependencies
sudo apt install ca-certificates curl gnupg

# Add the official GPG key
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# Add the repository
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# Install
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Verify the installation:

sudo docker run hello-world
sudo docker compose version

Project structure

Create a directory for your site (mkdir -p ~/web-app && cd ~/web-app). For example, for a WordPress site or static app, the file structure will be crucial for maintenance.

/home/monuser/web-app/
├── docker-compose.yml
├── .env
├── nginx/
│   └── conf.d/
│       └── default.conf
├── app/
│   └── (your source code here)
└── data/
    └── (for databases or uploads)

Step 4: Deploying a site (Concrete cases)

Case A: Static Site (HTML/CSS/JS or generator)

This is the simplest and most performant case. You only need an Nginx container serving static files.

Create a docker-compose.yml file:

version: '3.8'

services:
  web:
    image: nginx:alpine
    container_name: mon_site_statique
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./app:/usr/share/nginx/html:ro
      - ./nginx/conf.d:/etc/nginx/conf.d
      - /etc/letsencrypt:/etc/letsencrypt:ro

Note: the mounted nginx/conf.d folder replaces Nginx’s default configuration. Create a default.conf file in it with a server block (listen 80;, listen 443 ssl;, root /usr/share/nginx/html;, ssl_certificate /etc/letsencrypt/live/monsite.com/fullchain.pem;, ssl_certificate_key /etc/letsencrypt/live/monsite.com/privkey.pem;), otherwise the site is not served. The certificates, obtained on the host in Step 5, are mounted read-only. For robust production, it is better to use a dedicated reverse proxy (see below).

Case B: WordPress with Docker

WordPress is more complex as it requires a database. Let’s use MariaDB and PHP-FPM via Nginx. First create, in /home/monuser/web-app/, the .env file defining the passwords used below (DB_ROOT_PASS=... and DB_PASS=..., with strong values), and the uploads.ini file containing your PHP directives (e.g. upload_max_filesize): if it does not exist, Docker creates a directory in its place.

version: '3.8'

services:
  db:
    image: mariadb:10.6
    container_name: wp_db
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASS}
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wp_user
      MYSQL_PASSWORD: ${DB_PASS}
    volumes:
      - db_data:/var/lib/mysql
    networks:
      - wp_network

  wordpress:
    image: wordpress:php8.2-fpm
    container_name: wp_app
    restart: always
    depends_on:
      - db
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: wp_user
      WORDPRESS_DB_PASSWORD: ${DB_PASS}
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - wp_data:/var/www/html
      - ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini
    networks:
      - wp_network

  nginx:
    image: nginx:alpine
    container_name: wp_nginx
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - wp_data:/var/www/html
      - /etc/letsencrypt:/etc/letsencrypt:ro
    depends_on:
      - wordpress
    networks:
      - wp_network

volumes:
  db_data:
  wp_data:

networks:
  wp_network:

In this scenario, you must configure an nginx.conf file in the nginx/conf.d/ folder to forward PHP requests to the PHP-FPM container with fastcgi_pass wordpress:9000; (not proxy_pass: PHP-FPM speaks FastCGI, not HTTP). This is the standard method for running PHP in Docker.

Step 5: HTTPS and Let’s Encrypt Certificates

Having a site over HTTP in 2026 is unacceptable. Browsers mark insecure sites as “Not Secure,” which affects SEO and user trust.

The most reliable method is not to generate certificates inside each container, but to use a Reverse Proxy or an external tool that handles automatic renewal.

To keep things simple and without heavy external dependencies, we will use a standard Nginx configuration coupled with Certbot, but in a secure manner.

However, the current best practice is to use Cloudflare Tunnel (if you use Cloudflare) or to configure Certbot in standalone or webroot mode.

Here is how to obtain a valid SSL certificate for your domain monsite.com:

  1. Install Certbot on the host (not in a container to simplify port 80/443 management):

    sudo apt install certbot
  2. Stop Nginx if you installed it manually, or ensure it is not listening on 80/443 yet.

  3. Obtain the certificate:

    sudo certbot certonly --standalone -d monsite.com -d www.monsite.com
  4. Configure Nginx (in your container or host) to use the fullchain.pem and privkey.pem certificates from the /etc/letsencrypt/live/monsite.com/ folder (mounted read-only in the container), then start the stack from /home/monuser/web-app with sudo docker compose up -d.

  5. Automate renewal: Certificates expire every 90 days. Create a cron job on the server:

    sudo crontab -e
    # Add this line to check every day at midnight
    0 0 * * * /usr/bin/certbot renew --quiet --pre-hook "docker compose -f /home/monuser/web-app/docker-compose.yml stop" --post-hook "docker compose -f /home/monuser/web-app/docker-compose.yml start"

The Nginx containers in the previous examples mount /etc/letsencrypt read-only: they directly use the certificates obtained and renewed by Certbot on the host.

Step 6: Backups and Recovery

A server without backups is a server waiting for disaster. In self-hosting, you are your own disaster recovery team.

3-2-1 Strategy

  • 3 copies of your data.
  • 2 different storage media (the VPS disk + an external cloud).
  • 1 off-site copy.

Technical implementation

For a VPS, critical data includes:

  1. Your application files (app/, wp-content/uploads).
  2. The database.
  3. Configurations (docker-compose.yml, Nginx configs, SSH keys).

Simple backup script (backup.sh):

#!/bin/bash
DATE=$(date +%Y-%m-%d)
BACKUP_DIR="/home/monuser/backups"
REMOTE_DIR="user@remote-server:/backups"
source /home/monuser/web-app/.env
mkdir -p $BACKUP_DIR

# 1. Database backup (MySQL example)
docker exec wp_db mysqldump -u root -p${DB_ROOT_PASS} wordpress > $BACKUP_DIR/db_$DATE.sql

# 2. Archive files
tar -czf $BACKUP_DIR/files_$DATE.tar.gz /home/monuser/web-app
docker run --rm -v web-app_wp_data:/data:ro -v $BACKUP_DIR:/backup alpine tar -czf /backup/wp_data_$DATE.tar.gz -C /data .

# 3. Upload to remote storage (SCP or Rclone to S3/Backblaze)
scp $BACKUP_DIR/* $REMOTE_DIR/

# 4. Local cleanup (keep only 7 days)
find $BACKUP_DIR -type f -mtime +7 -delete

Make this script executable and schedule it via sudo crontab -e, since it calls docker, which requires root (e.g., every day at 2 AM). Sending to object storage (S3 compatible) is highly recommended as it is inexpensive and can be made immutable by enabling Object Lock.

Which choice for your profile?

We have seen the technical steps. But technology is just a means. Here is how to choose your approach based on your reality.

Profile 1: The Curious Beginner

  • Goal: Learn, host a simple blog.
  • Choice: Entry-level VPS (€3-5/month).
  • Stack: Docker Compose + Static Site (Hugo/Jekyll) or pre-configured WordPress.
  • Why? Less maintenance, fewer security risks related to misconfigured PHP/MySQL. Static is indestructible.

Profile 2: The Developer / Freelancer

  • Goal: Host your own apps, dynamic portfolios, APIs.
  • Choice: Mid-range VPS (€8-15/month).
  • Stack: Advanced Docker Compose, Reverse Proxy (Traefik/Nginx), Monitoring (Prometheus/Grafana or Uptime Kuma).
  • Why? You have the skills to manage updates and security. You want the flexibility to deploy any language.

Profile 3: The Enterprise / Critical Site

  • Goal: High availability, high traffic, compliance.
  • Choice: High-performance VPS or Managed Cloud (Kubernetes, AWS).
  • Stack: Infrastructure as Code (Terraform), CI/CD (GitHub Actions), Managed Database (Neon, AWS RDS) to delegate DB management.
  • Why? The time spent managing a “home” VPS is too costly compared to the revenue generated. Outsource infrastructure complexity.

FAQ: Frequently Asked Questions

Can I migrate from WordPress.com to my own VPS?

Yes, but be aware of the complexity. WordPress.com (SaaS) handles updates, security, and caching. On your VPS, you have to do everything yourself. Use WordPress’s native export/import tool to retrieve your posts and media. Think about configuring a fast cache (Redis or Varnish) and a CDN (Cloudflare) to compensate for the performance loss of the SaaS.

How long does the initial setup take?

If you are a beginner, count 4 to 8 hours for the first installation, including understanding Docker, SSL configuration, and SSH hardening. Once the base is in place, deploying a new site takes 15 minutes. The learning curve is steep at first, then flattens out.

Is Docker mandatory?

No, but it is highly recommended. You could install Nginx and PHP directly on the OS (apt install nginx php-fpm). However, this creates a “snowflake server” (a unique and hard-to-reproduce server). If the server crashes, you have to reinstall everything manually. With Docker, you restore your server by recreating containers from a docker-compose.yml file. It is a matter of reproducibility and cleanliness.

What if my VPS is hacked?

Prevention is key (OS updates, fail2ban, SSH keys). If the worst happens, do not try to “clean” the server. Start from a clean image (snapshot), restore your data from the last known good backup, and rebuild the infrastructure. Reinstallation is often faster and safer than detoxifying a compromised system.

Conclusion

Hosting your own website in 2026 is a rewarding technical exercise that offers total control over your data and costs. Going through a VPS and Docker represents the current standard for combining performance, security, and ease of maintenance.

Do not underestimate the initial hardening phase and the setup of backups. These two pillars distinguish an unorganized hobbyist from a professional self-hosting practitioner. Start small, document your procedures, and do not hesitate to iterate. Your infrastructure is your code; treat it with the same rigor.

Tags: VPSDockerHTTPSLet's EncryptSelf-HostingWeb HostingCloudSecurityDevOps2026 Guide

Related

Tutorials

Host Vaultwarden with Docker and HTTPS in 2026 (Complete Tutorial, Caddy Reverse Proxy)

Step-by-step 2026 guide to self-host Vaultwarden with Docker. Secure deployment, automatic HTTPS via Caddy and Let's Encrypt, protected admin panel, 2FA, encrypted backups, and hardening. Ready-to-use docker-compose and Caddyfile configs.

Read
Tutorials

Automated HTTPS Reverse Proxy with Caddy and Docker in 2026 (Effortless Let's Encrypt Certs)

2026 tutorial on deploying a Caddy reverse proxy with Docker: automatic HTTPS and Let's Encrypt certificates, multi-service routing, security headers, and seamless renewal. Ready-to-use Caddyfile and docker-compose configs.

Read
Tutorials

Deploy Immich with Docker in 2026: Complete Guide (HTTPS, Reverse Proxy, Backups)

Step-by-step 2026 tutorial to self-host Immich, the Google Photos alternative, using Docker Compose. Features include photo/video gallery, facial recognition, automatic HTTPS via Caddy and Let's Encrypt, backups, and hardening. Ready-to-use configs included.

Read