Okay, site's up on a VPS (per [previous post](/blog/fuller-stack/0026-the-quest-for-having-a-mailing-address)). Let's talk about it.

First of all, I have a convention of naming my various computers after female characters (especially champions) from the Runeterra universe. My site is normally hosted on [Morgana](https://universe.leagueoflegends.com/en_US/champion/morgana/). My daily driver personal laptop is [Katarina](https://universe.leagueoflegends.com/en_US/champion/katarina/). My work computer, until quite recently, was [Renata](https://universe.leagueoflegends.com/en_US/champion/renataglasc/). So when I needed a hostname for a new remote machine, it occurred to me that Alune would be an excellent name, both aesthetically and for [lore reasons](https://universe.leagueoflegends.com/en_US/story/champion/aphelios/). While not a champion herself, her whole deal is providing assistance from far away. My Alune is just in Germany instead of the spirit realm.

I ended up going with [NetCup](https://www.netcup.com/en) as my hosting service. I'd heard good things, and while I wish they wouldn't email initial credentials (gosh, what a pressure to replace them ASAP!) it's been a pretty painless process, and even their lightest VPS offering was still going to be overkill for my needs. I still needed to use the VPS option, even though they offer web hosting, because it wasn't possible to tell up-front how much customization power I'd have over the configuration, and I have some fun features like Markdown-over-curl to support. So a few bucks (err... euros, actually) extra per month, but not too bad. And unlike Codeberg, I could pay with my filthy American money.[^1]

I was also able to save by picking the "somewhere in Europe but I'm not picky" location option. Honestly, it's kind of neat having a server far enough to have noticeable latency in Vim for legitimate, non-performance reasons. Another place I looked to save money was by opting out of IPv4 support and going for a purely IPv6 stack. This complicated things.

Now, I didn't expect it to complicate things, because my intention was to host the website as identically as possible to my existing setup on Morgana (minus the FreshRSS that wasn't worth migration effort): use a CloudFlare tunnel in a sibling Docker container as an encrypted pipe for talking to the CloudFlare mothership, which allows me to host a website off of any computer that can handle outbound traffic to the public internet. Surely that can handle IPv6, right?
## Docker being a weak link

I find that Docker is something that, in a general sense, I used to really love, but I've been liking it a little less every year for a couple years in a row. This is gonna be one of those moments.

The out-of-the-box experience for IPv4 networking on Docker is honestly quite good for simple use-cases. [My website usually doesn't need a custom Docker network at all](https://git.sr.ht/~maddiem4/mm4cc-web/tree/main/item/docker-compose.yml), relying on the defaults for some predictable, sensible behavior that doesn't require me to think too hard. This hits an abrupt wall of "aw heck, I have to think about everything" as soon as you get out of these friendly 32-bit pastures and into the 128-bit hyperspace of IPv6.[^2]

I'd probably look very cool if I deeply understood why my bridge network wasn't able to talk to the public internet, and how to solve it the right way. Is it making poor choices which IPs to allocate for containers within the bridge? Do I need to customize the subnet? I'd like to understand it someday. However, in my circumstances, I am both

1. Under time pressure to get hosting off of Morgana so I can pack her into storage for a few months and Mrs. Gorbachev Tear Down This Desk, and
2. Doing the kind of work where there isn't actually reason to avoid host networking anymore.

So. After futzing around with manual container startup to confirm that host networking does entirely circumvent whatever shenanigans are afoot, I settled on the following arts-and-crafts arrangement of duct tape and Sharpie.

First of all, I'm running the CloudFlare tunnel in host networking mode, using the IPv6 version of the tunneling service:

```yaml
  tunnel:
    image: cloudflare/cloudflared
    env_file: ${SITE_MODE}.env
    command: --edge-ip-version 6 tunnel run
    restart: unless-stopped
    network_mode: "host"
```

Secondly, I made sure that I was publishing the `app` container's port 80 to actual host port 80 with my `.env` file:

```sh
SITE_MODE=prod
PORT=80
```

This gets interpreted in `docker-compose.yml` for the `app` service, replacing the default of 8082:

```yaml
  app:
    image: nginx
    restart: unless-stopped
    volumes:
      - ./site:/usr/share/nginx/html:ro
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
    ports:
      - ${PORT:-8082}:80
```

And finally, I made sure that `app` was an alias for `localhost` in my `/etc/hosts` file. I did this for both IPv4 and IPv6 because I didn't want to edit twice, in case I needed one or the other specifically to work.

What this adds up to is, the CloudFlare tunnel is still running in Docker and receiving mothership orders to route to `app:80` internally, which just ends up being `localhost:80` which is exposed through boring old port publishing. It is now a very close experience, from a networking perspective at least, to just running both Nginx and the tunnel as non-containerized processes. But I still have an easier time with initial software download and updates by using public Docker images, so there's some having my cake and eating it too, here, I'm just using fewer of Docker's features to avoid the edges where some of them don't work.

This is... kinda stupid. It works, it shouldn't be any kind of security problem for what I'm doing, and it's saving me a lot of time in a critical moment. But should you do this? I'm going to say probably not! Under normal conditions, _I_ probably wouldn't do this in such a sloppy way. But it's good enough for a temporary server that just needs to putter along cheaply and simply for a couple months before I switch back to Morgana again.

[^1]: This was a very real point of friction that led to me discovering and enjoying Sourcehut instead. Wanna know why I put my code on sr.ht when I was looking to reduce my Github participation? Now you know.

[^2]: I need to stop coming up with ideas for posts to put in the backlog, but I think at some point I need to do the research necessary to precisely and educationally put IPv6 on blast for the specific ways it stole defeat from the jaws of victory through overengineering. There's a pretty big gap between what anyone wanted (literally just more address space) and what we got. That said, technologies like Docker should be reasonably expected to work with the IPv6 we got, in the 2026 we're living through.