Skip to content

Single-Server Deployments with Terraform, Docker Compose, and GitHub Actions

A single cloud server can affordably power a range of services. In many cases, the vertical scalability capabilities of a single host are enough to cover the needs of many business applications for years, if not indefinitely. Deploying on top of plain cloud servers requires more expertise and oversight and has different risks and costs than relying on popular PaaS offerings like Heroku. There are also tools like Dokku that facilitate deploying to a single server with a good developer experience. But let's assume that you don't want to use any of these, but prefer to build your deployment pipeline on top of lower-level building blocks like Terraform, an infrastructure provisioning tool, Docker Compose, a multi-container manager, and GitHub Actions, a CI/CD automation tool. This post shows one way to do that - ok, cheating a little by also relying on Docker Hub, S3, Route53, and Let's Encrypt for convenience, but still keeping recurring hosting costs minimal.

Automating Partitioned Table Migrations with GitHub Actions

PostgreSQL supports table partitioning, which splits rows into multiple partitions for improved query performance and easier bulk data transfers. Partitioned tables are useful when data are regularly inserted into a table and only recently added rows are selected in active operation. Older table partitions can be detached, archived, and finally dropped. Changes to database schemas in production systems should be managed with schema migrations, version-controlled, and incrementally applied SQL scripts. Repeated schema migrations should be automated to avoid errors, save engineering time, and ensure they are run. GitHub Actions is a CI/CD automation service that executes jobs according to workflow definitions. This post shows how to use GitHub Actions to regularly open pull requests that manage table partition migrations.

React SSR Memory Consumption

Server-side rendering (SSR) can help improve core web vitals and is essential for SEO. React and node.js are often used to server-side render web pages. However, under high concurrency, rendering complex web pages may increase memory consumption and cause the application to crash if memory allocation fails. This post explores the following topics:

  • Measuring node.js memory consumption as increasingly complex web pages are rendered with React.
  • Measuring node.js memory consumption and throughput when serving HTML pages in an HTTP server.
  • Limiting node.js concurrency with haproxy.

Update 2022-11-11: Please check out the article Optimizing SSR Memory Usage on wolt.com, which discusses the topic more thoroughly.

TypeScript Conditional Type Inference in Function Parameters

Conditional Types are type-level conditional expressions of the form:

SomeType extends OtherType ? TrueType : FalseType;

TypeScript can infer types for expressions in many cases, but it seems to be unable to do so for generic functions with type parameters involving conditional types, at least in version 4.7.4. This post illustrates when such type inference could be useful and how to nudge TypeScript to infer types correctly.

Injecting Hooks Into React Components

Dependency Injection is a design pattern providing dependencies to a function (or class) in call sites rather than importing them directly in the implementation. Using the pattern it is easier to supply different implementations for dependencies depending on the call site (e.g., modular code reuse, tests, and component explorer). A loosely coupled codebase can be more maintainable. Hooks are used for writing stateful React components without introducing a class. This post explores three ways how to inject hooks to React components instead of importing them:

  • passing hooks in props
  • currying hook parameters (component factories)
  • react-facade, passing hook implementations through Context

Update 2023-03-31: Please check out the improved version of the article at Wolt Careers Engineering Blog, which discusses the topic more thoroughly.

Page 4 of 4
Older posts →