Hemlock Labs Logo

I'm scheduling new projects for 2026—availability is limited. Get in touch to see if we're a good fit.

Case Study: Hardcover.app Infrastructure Modernization

What happens when your passion project starts getting popular? For Hardcover.app, it meant infrastructure growing pains. I stepped in to modernize their setup with Infrastructure as Code and GitOps, bringing the stability and simplicity needed to eliminate their founder as a bottleneck and reduce their overall cloud bill.

Case Study: Hardcover.app Infrastructure Modernization

Hardcover.app is a passion project for book lovers, built by a small, dedicated team in their spare time. It's not just a place to track reading habits, it also provides a free, extensive API for book data. Recently, Hardcover has seen an increase in users and API traffic. While they celebrate the success of gaining users, they were in need of infrastructure that could truly scale and keep up with their growth.

Their existing infrastructure was a tricky combination of Heroku and a few servers (droplets) running on DigitalOcean. I sat down with Adam Fortuna, one of the founders, to come up with a plan. Adam wasn't just worried about scale, he needed to stop being the bottleneck. At the time, only he could deploy new features, which meant he spent more time babysitting the servers than building the app.


The old infrastructure

The Hardcover application stack is built on a solid foundation, but it's not simple. It's a highly functional system with several distinct services, from the search database (Typesense) to the image processing pipeline and the worker queues handling resource-heavy tasks like importing massive book lists. If you want to see the full technical breakdown, Adam covered some of it in the Hardcover November 2025 blog post.

All of this complexity led to real day-to-day challenges for the small team to keep servers running and to manage spikes in traffic. Adam had five key problems he wanted to include in the plan.

  1. Search Database issues: Their search database (Typesense) was running on a single server and when it had issues it needed to be manually restarted. This left the search functionality completely unavailable for 5 to 10 minutes. This was a large drop in user experience and confidence.
  2. Manual SSL certificate management: Managing SSL certificates was a confusing, manual chore across different parts of the app. It was a constant source of anxiety waiting for a certificate to expire and suddenly throw security warnings at their users.
  3. Deployment friction: Their old process used Ansible and demanded manual SSH key management for every developer. Plus, if they needed more server capacity (scaling up), someone had to manually spin up a new server. It was slow and cumbersome.
  4. Log consolidation: Logs weren't aggregated anywhere, you had to manually SSH into the exact server running that component to pull the log file. Getting centralized logging (and ideally, metrics) was critical for finding issues before users saw them.
  5. The DigitalOcean Mandate: They wanted to stay on DigitalOcean. As a book-focused app, Hardcover was keen to stick with a smaller, more sustainable cloud provider and avoid the major market cloud providers.

The modernization plan

I love tackling these kinds of modernization projects, especially when the goal is to provide huge savings and efficiency gains on a platform as startup-friendly as DigitalOcean. The solution was a dramatic shift to an automated system using Infrastructure as Code (IaC) and GitOps.

At a high level, I moved everything onto DigitalOcean Kubernetes (DOKS) and created GitHub action workflows to handle all deployments. To manage it all, I created two distinct GitHub repositories, the “infrastructure” repository for the infrastructure itself, and the "deployments" repository for the app deployments. This split keeps everything clean, audit-friendly and simple to onboard new team members.

Terraform: Where infrastructure is defined

I used Terraform as the single source of truth for the entire infrastructure. Think of it as a blueprint: it defines every Droplet, network, Spaces bucket, and the DigitalOcean Kubernetes (DOKS) cluster in a way that can be reviewed before any infrastructure is provisioned.

A big win here was making changes safe and visible. I set up GitHub Actions to automatically run and post the Terraform plan on every pull request. This means anyone reviewing the code can see, in plain text, exactly what will be added, changed, or deleted before it goes live. This removes all the stress and guesswork from infrastructure changes.

Deployments: The repeatable deploy recipe

The second repo handles the deployments; these are the configurations for getting the applications running on DOKS. This is how I brought order to application deployments into a single place.

The first step to a reliable deployment process is to ensure an environment exists that can be used to test changes to infrastructure and applications alike. Hardcover now operates across two separate environments:

  • Production: This is the live user-facing environment with autoscaling that is tuned for performance and stability.
  • Staging: This environment is identical to the production environment. The team can now test changes and features without ever needing to go to production. This eliminates fears around deploying to production and impacting users.
  • Metrics: A separate, cost-effective cluster dedicated to internal tooling. This runs the Grafana stack for centralized logging and metrics. Putting this essential monitoring outside of the main clusters ensures the team can always debug and see what's happening, even if the main Production cluster is under heavy load.

The Power of GitOps and Automation

I moved all application deployment configuration onto DOKS using Helm. Helm allows us to simplify deployment configuration so that you don't need to be a Kubernetes expert to deploy an app. This ensures the team can deploy new applications in the future in a consistent, easy way. No more relying just on Adam.

I implemented a GitOps workflow. This means the infrastructure code (Terraform) and the deployment code (Helm) are the only source of truth. New applications are deployed through a single, secure process in GitHub actions. Now all a team member has to do to deploy is to select an app, an environment, a branch name and push deploy. This single workflow ensures that deployments are simple, consistent, and fully auditable by the whole team.

  • Typesense is fixed: I deployed the Typesense search database as a resilient three instance cluster. Now, if one node needs a reboot, the others seamlessly take over. The search feature never goes down.
  • No More SSL Worries: I deployed cert-manager inside the cluster to fully automate the management and renewal of all SSL certificates. The confusion is gone, everything is consolidated and certificates automatically renew before they expire.
  • Intelligent Worker Scaling: I solved the issues with heavy resource tasks (like book list importing) using KEDA (Kubernetes Event-Driven Autoscaling). Instead of a handful of workers running 24/7 and wasting money, KEDA intelligently monitors the worker queues and only scales up the workers when a job needs doing. This allows Hardcover to scale to many more workers than used previously to get jobs done faster, but scale back to zero when they aren’t needed.
  • Security & Secrets: I solved the SSH key management issues by removing them completely and moving to encrypted secrets using SOPS. Now, the secrets live safely in the deployment repo, but only the automated pipeline can decrypt them.

Results: The good stuff

With the new infrastructure finalized, the Hardcover team was able to immediately shut down all legacy assets on Heroku and AWS. Everything is running smoothly within their new, organized DigitalOcean Kubernetes environment. This wasn't just a technical upgrade, it was a fundamental shift in how the team operates. Here are the biggest wins:

Massive Cost Reduction

Despite adding a full Grafana stack for centralized monitoring and nearly doubling their production capacity, the team saw immediate and sustained cost savings.

  • 30% Decrease in Total Infrastructure Cost. This was possible by eliminating 24/7 fixed servers and leveraging KEDA's intelligent worker scaling, which only pays for compute time when a resource intensive book import job is actually running.
  • Zero Heroku/AWS Overhead. Shutting down these services simplified billing and eliminated the "mystery charges" that come with managing sprawl across multiple platforms.

Freedom from the Bottleneck

The new GitOps workflow finally addressed Adam's biggest headache: being the sole gatekeeper for deployments.

  • Deployments Simplified. Other team members can now easily deploy pull requests to the staging environment, dramatically accelerating the testing and feedback loop.
  • Freedom for Adam. Best of all, new features have started being shipped to production without any help from Adam. His time is now focused entirely on building new features for book lovers, not babysitting servers. The fear of making a mistake is gone because every change is defined in code and reviewed before it goes live.

Stability and Predictability

The instability and logging issues are a thing of the past.

  • 100% Search Uptime. The new resilient Typesense cluster ensures that the search functionality is never unavailable for those 5-10 minute blackouts.
  • Full Visibility. The dedicated Metrics cluster provides centralized logging and metrics, allowing the team to quickly spot and fix issues before they impact users, no more hunting down logs on individual servers.

This modernized infrastructure didn't just save Hardcover money, it gave the small team the freedom and confidence to focus on growth. It transformed infrastructure from a bottleneck into a reliable, automated asset.


Does this story sound familiar? If your small business is looking to remove deployment bottlenecks and optimize its infrastructure spend with a modern, automated solution, feel free to reach out. I'd love to chat!