In this guide I will try to document 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 fairly straight forward process; depending on what your expectation is, it is possible to do it in different levels of complexity. This guide focuses on the relatively simple method (as opposed to a more comprehensive “homelab” running services like Nextcloud—explained here), which uses a Linux computer that could be your old laptop you keep turned on always, or an old desktop PC, mini-PC, a single board computer like a Raspberry Pi, or basically anything that can run a Linux distro and has enough juice to run two Docker containers. Docker is a containerization (virtualization) system, and an alternative to it is Podman, and both of them are part of the Open Container Initiative. This means you can use the same container images with both of them.
Before getting into the setup, the first thing you have to check is related to your internet connection at home, or where you want to setup the server for your website. Generally, to be able to setup self-hosting a 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 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, which your home server connects to with its dynamic IP address, and you direct visitor traffic to your website via this “gateway,” which is one way to solve the problem. Another solution is to use a tool which updates your DNS records, every time your dynamic IP address changes. The former is a simpler solution, as it does not require a second, external server.
The second issue, which you have to 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 which is visible to the outside traffic, is different from the one that you see in the admin panel of your modem. If these IP addresses are different from each other, it means your ISP is using something like a double-NAT, which is an issue when you want your home server accessible to the outside traffic. To understand if you are behind a double-NAT, you can go to a website that shows you your IP address, like whatismyip etc. Take a note of that IP address. Then, login to your modem's admin panel (which is 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 that is shown there. If the two IP addresses, the one that is shown by “whatismyip-type-of-website” and the one you see on your modem's admin panel are different than each other, 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 also, it is possible to solve that with some tunneling (more on that here, and here).
Back to the issue with double-NAT though, and there are multiple solutions to this problem. The first and usually simple one is to call your ISP, and ask them not to use double-NAT, or to tell them that you need an IP address. The issue is that they sometimes allocate one public IP address to multiple customers, and share this one IP in between them. But when you ask them not to do that, most of them say OK, and give you an IP address, which is still dynamic, but at least yours. Some do this for free, 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:
At Well Gedacht Publishing, we also had this issue with the ISP sharing an IP address with multiple households, and were able to get a dedicated IP—yet a dynamic one—after calling their customer service. Currently, our self-hosted websites and services run on our home server, without any VPS or another type of solution to the dynamic IP address problem. This 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. Every once in a while, 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 the uptime of your sites, like this one.
# 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 the 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 brower, 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: