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:
- Global External Load Balancer - A single ingress point that routes all incoming traffic.
- Cloud Run - A single serverless container running nginx that serves all of our static sites.
- Google Cloud Certificate Manager - Centralized management for all our SSL certificates using a single certificate map (
siocode-static-certs). - 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.conffile, properly routing every domain to its specific build directory. - The
build.shandbuild.batscripts, which clone the repositories, run their specific build commands, and output everything into the centralbuild/folder. - The
test_domains.shandtest_domains.batscripts, 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!
