This guide documents how we host our static websites at Well Gedacht Publishing. Here is an example, and another one, and another one.
Setting up something like a home server is a relatively straightforward process; depending on your expectations, it is possible to do it at different levels of complexity. This guide focuses on the relatively simple method (as opposed to a more comprehensive “homelab” running services like Nextcloud—explained in our separate guide), which uses a Linux computer that could be your old laptop you always keep powered on or an old desktop PC, mini-PC, a single board computer like a Raspberry Pi, or anything that can run Linux and has enough juice to run two Docker containers. Docker is a containerization (virtualization) system, and an alternative to Docker is Podman, and both are part of the Open Container Initiative. You can use the same container images with Docker and Podman.
Before getting into the setup, you first have to check your internet connection at home or where you want to set up the server for your website. Generally, to set up a self-hosting website and make your server accessible to traffic on the internet, you need to have a static IP address. This address is then used when someone wants to visit your website. Most, if not all, internet service providers, though, do not give their customers static IP addresses; the static ones are usually reserved for “enterprise,” and it is not possible to get with typical customer internet plans. But no worries, there are still some ways to overcome this issue. You can use VPS, a virtual private server that your home server connects to with its dynamic IP address, and you can direct visitor traffic to your website via this “gateway,” which is one way to solve the problem. Another solution is to use a tool that updates your DNS records every time your dynamic IP address changes. The latter is a more straightforward solution, not requiring a second external server.
The second issue you must pay attention to before setting up everything is to understand if your ISP is using a system called double-NAT or CG-NAT. If that is the case, your public IP, visible to the outside traffic, differs from the one you see in your modem’s admin panel. If these IP addresses differ, your ISP uses a double NAT, which is an issue when you want your home server accessible to outside traffic. To understand if you are behind a double NAT, visit a website showing your IP address, like Whatismyip. Take note of that IP address. Then, log in to your modem’s admin panel (usually done by typing 192.168.X.X—you can find the correct address by googling your modem’s brand and model) and look for the IPv4 address shown there. If the two IP addresses, the one shown by “whatismyip-type-of-website” and the one you see on your modem’s admin panel, are different, this means your ISP is using a double-NAT type of thing.
I think some ISPs let their customers use an IPv6 address, which—I believe—also makes it possible to do what we want, but as I have no experience with it, I cannot elaborate much on that. The issue with self-hosting a website with IPv6 is that only people with IPv6 addresses can access your site, but it is also possible to solve that with some tunneling (more on that here, and here).
There are multiple solutions to the issue with double-NAT, though. The first and usually simple one is to call your ISP and ask them not to use double-NAT or to tell them you need an IP address. The issue is that they sometimes allocate one public IP address to multiple customers and share this one IP between them. But when you ask them not to do that, most say OK and give you an IP address, which is still dynamic, but at least yours. Some do this for free, while others might charge you a fee.
Another way to tackle the issue related to your ISP using double-NAT or CG-NAT and not having an accessible public IP address for your home internet is again to use a VPS or a VPN like these: Self-hosted Gateway, a self-hosted VPS solution, or WireGuard, a FOSS VPN software (or Tailscale, a proprietary alternative based on WireGuard, or Netbird). A VPN like WireGuard does not solve the problem itself, but more on that later.
At Well Gedacht Publishing, we also had this issue with the ISP sharing an IP address with multiple households. Still, we got a dedicated IP—yet dynamic—after calling their customer service. After solving the double-NAT issue, our self-hosted websites and services run on our home server, but there is no solution to the dynamic IP address problem. That means that once our ISP decides to change our IP address, for example, if we restart our router, or the electricity goes out, etc, our IP address changes too, and in that case, we would need to update the DNS records on our registrars ourselves; also meaning that our websites will be inaccessible if we don’t update the IP address on the DNS records, which is something we are OK with. Occasionally, your websites might have some downtime, and if you are OK with this, you can even self-host with your dynamic IP address. There are also tools for monitoring your sites’ uptime, like Uptime Kuma and updating your DNS records automatically when they are changed, like DDNS Updater Docker container.
# Add Docker's official GPG key: sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc # Add the repository to Apt sources: echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null sudo apt-get update
Install the Docker packages:
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Verify that the installation is successful by running the hello-world image:
sudo docker run hello-world
sudo docker run -d -p 9000:9000 --restart always -v /var/run/docker.sock:/var/run/docker.sock portainer/portainer-ce:latest
mkdir ~/site-content
sudo docker run -it --restart always -d -p 8080:80 --name web -v ~/site-content:/usr/share/nginx/html nginx
This command tells this Nginx container to listen for upcoming traffic on port 8080, and the content of your website, such as the index.html goes to the directory ~/site-content, and the name of the container is “web.” You can check if Nginx is succesfully deployed and running by typing your-computer's-IP-address:8080 on your browser, and it will show you this page:
mkdir -p npm cd npm
Then, create a Docker compose file there:
# Create a docker-compose configuration file touch docker-compose.yml # Editing the file nano docker-compose.yml
Put these inside the docker-compose.yml:
version: '3.8' services: app: image: 'jc21/nginx-proxy-manager:latest' restart: unless-stopped ports: - '80:80' - '81:81' - '443:443' volumes: - ./data:/data - ./letsencrypt:/etc/letsencrypt
Finally, you need to run:
docker compose up -d
Now, your Nginx Proxy Manager admin UI should be accessible at your Linux PC's IP address on your local network, on port 81 (something like 192.168.186.10:81).
Here I will document my solution to dynamically update the dynamic IP address on the DNS records of my domain. Using something like this. Coming up soon!
— Well Gedacht Publishing 2025/02/21 14:40
Nice tools which are not in the scope of this guide, or other guides for self-hosting: