SIO Web Hosting Infrastructure v2: Cheaper, Faster, and Simpler

If you read our previous article on the SIO Web Hosting Infrastructure, you know that we maintain a large portfolio of static websites and web applications—everything from our company sites to the growing collection of "Helpful Things" at sio.sh.

While our original GitOps-powered infrastructure relying on Compute Engine VMs and nginx served us well, we realized there was still room for improvement in terms of cost efficiency, operational overhead, and scalability. Today, we're excited to introduce the next iteration of our hosting setup: SIO Web Hosting Infrastructure v2.

The Evolution: From VMs to Cloud Run

The biggest shift in our v2 architecture is the move away from dedicated Compute Engine VMs for ingress. Instead, we have streamlined the entire pipeline down to a few managed Google Cloud components:

  1. Global External Load Balancer - A single ingress point that routes all incoming traffic.
  2. Cloud Run - A single serverless container running nginx that serves all of our static sites.
  3. Google Cloud Certificate Manager - Centralized management for all our SSL certificates using a single certificate map (siocode-static-certs).
  4. Artifact Registry - For storing our versioned Docker images.

By utilizing Cloud Run, we completely offload server management and scaling to Google Cloud. We only pay for what we use, which has resulted in a staggering 70% reduction in operating costs compared to running our own VMs 24/7.

How It Works

All of our static apps are compiled into a single unified /build directory. We then build a lightweight Docker image based on nginx:latest, containing our configuration and the compiled sites.

The Load Balancer receives all traffic and forwards it to the Cloud Run service. Inside the container, nginx handles the routing to the appropriate folder based on the requested domain.

The Magic of ProJor

Managing dozens of domains, build scripts, and nginx server blocks manually would be a nightmare. This is where ProJor, our model-based code generator, shines.

In our repository, we maintain a .projor/ directory containing YAML files that define our repositories and individual sites (reposv2.pdata.yaml and sitesv2.pdata.yaml). Using ProJor, we automatically generate:

  • The nginx.conf file, properly routing every domain to its specific build directory.
  • The build.sh and build.bat scripts, which clone the repositories, run their specific build commands, and output everything into the central build/ folder.
  • The test_domains.sh and test_domains.bat scripts, which query Google Cloud to ensure our Load Balancer and Certificates are correctly configured for all domains.

When we need to add or remove a site, we simply update the YAML model and run npx @projor/cli generate. ProJor updates all the complex scripts and configuration files instantly, ensuring zero human error in our routing rules.

Faster Deployments

The move to a unified container has also streamlined our deployment process. We simply push a new image to Artifact Registry and update the Cloud Run service revision.

For large-scale updates, our CI/CD runs the full build.sh script to rebuild everything. However, for smaller updates (changing fewer than 5 sites at once), we can now perform surgical deployments. We just run the specific local build commands for the affected sites, copy the new output into the local build/ directory, and push a new Cloud Run image. This hybrid approach saves us significant time while ensuring our deployments remain reliable.

We are incredibly happy with this new architecture, and we'll continue refining it as our ecosystem grows!