Custom Domain System Design
Create a custom domain system design using multi-tenant system architecture with an SQL database.
Welcome to Coderbased, a software engineering newsletter that helps you to become hands on tech leader. Every week we write about system design, leadership, software engineering mindset, tips to become better Software Engineer.
Before we start, kindly to subscribe, follow me on Linkedin, Twitter and share this content to your friends. Enjoy.
The custom domain feature enables you to use your own domain to access a service, even though, behind the scenes, it’s actually hosted by another service. This feature is commonly offered by SaaS platforms or B2B white-label solutions.
For a simple example, although you're accessing this website through coderbased.com, it’s actually hosted on Substack. Initially, the domain was coderbased.substack.com, provided when we registered our publisher account.
Today, we’re going to discuss how to design a system for a custom domain feature. This is an exciting topic, so let’s dive in.
Building a Custom Domain Feature
Imagine we’re building a Blog-as-a-Service platform similar to Substack, called Blogstraps, and we want to implement a custom domain feature. To understand this better, let’s walk through the user journey on Substack.
When you first create a Blogstraps account, you're provided with a subdomain—let’s say coderbased.blogstraps.com.
In the settings, you can then specify a custom domain, like coderbased.com, and you'll need to update your DNS records with certain values.
When you first create a Blogstraps account, you're provided with a subdomain—let’s say coderbased.blogstraps.com.
In the settings, you can then specify a custom domain, like coderbased.com, and you'll need to update your DNS records with certain values:
After this, you submit the request and wait for the DNS propagation
Once the DNS propagation is complete, the system processes the custom domain setup. On Substack, this process can take almost a day
How would you design a custom domain feature? Feel free to contribute and share your ideas by filling out the form at this link.
Now, let’s explore our proposed solution.
Build a Custom Domain Using Multi-Tenant System Architecture
Our solution uses the concept of a multi-tenant system architecture. Multi-tenancy allows multiple users or customer groups to access the same application or infrastructure. In MySQL or Postgres, this can be implemented by adding a tenant column. In this case, the domain column can serve this purpose.
Before we go further, we'll focus on how to use multi-tenancy to build the custom domain feature, without diving too deeply into the blog system.
Database Design
Let’s start with database design.
The most important tables and columns for implementing multi-tenancy in a custom domain feature are in the project
table:
domain
: Stores the Blogstraps subdomain (e.g., coderbased.blogstraps.com).custom_domain
: Stores the actual custom domain (e.g., coderbased.com). It’s nullable, meaning if it’s null, no custom domain has been set.custom_domain_status
: Tracks the status of the custom domain setup with values like:{status: "pending", domain: ""}
{status: "processing", domain: "coderbased.com"}
{status: "success", domain: "coderbased.com"}
When custom_domain
is null, the user accesses the blog via the domain
. If custom_domain
is set, they can use either domain
or custom_domain
. We can handle database access like this:
if HasSuffix(host, ".blogstraps.com")
SELECT * FROM project WHERE domain = {host}
else
SELECT * FROM project where custom_domain = {host}
// p.s: Here, host refers to the domain used by the user in their browser.
High Level Design
Before enabling a custom domain, if a user accesses coderbased.blogstraps.com, the request hits a reverse proxy (e.g., NGINX), which then queries Blogstraps' backend system. This is the typical request flow.
When the project owner sets up a custom domain (e.g., coderbased.com), the project table is updated. A worker process periodically checks for new custom domains to process. The worker checks if the DNS for coderbased.com points to the Blogstraps backend.
The owner must configure their DNS settings (using A or CNAME records) to point to Blogstraps' servers, as described in the earlier steps. Once DNS propagation is complete, Blogstraps prepares the custom domain’s route, including redirection, and sets up SSL (using Let’s Encrypt for simplicity).
Once the custom domain is set up, accessing coderbased.blogstraps.com will redirect to coderbased.com, and requests to coderbased.com will go through the Blogstraps backend.
At a high level, it may still be missing a lot of information. To understand more, let’s discuss each use case in detail.
System Behavior Before Custom Domain Setup
Before the custom domain is set, accessing coderbased.com won’t route to Blogstraps System, resulting in a 500 error. However, accessing coderbased.blogstraps.com will work as follows:
The request passes through Blogstraps' NGINX.
Blogstraps recognizes the host as coderbased.blogstraps.com.
It queries the database to fetch the articles:
// Query for accessing articles
SELECT a.* FROM article a
INNER JOIN project p on a.project_id = p.id
WHERE p.domain = {host}
Setting Up the Custom Domain
The process begins when the project owner sets the custom_domain (e.g., coderbased.com). At this point, the database reflects this change.
Next, the owner configures the DNS, directing coderbased.com to coderbased.blogstraps.com.
The background worker checks if the DNS has propagated, and once it has, NGINX configurations are updated:
/etc/nginx/sites-available/coderbased.com
/etc/nginx/sites-enabled/coderbased.com
At this stage, coderbased.com is connected to the Blogstraps system, but SSL is not yet set. Running sudo certbot --nginx -d coderbased.com
will secure the domain using Let’s Encrypt.
For more details, refer to this tutorial
Now, the database state reflects this change:
System Behavior After Custom Domain Setup
After the custom domain is set, accessing coderbased.blogstraps.com will trigger a redirect to coderbased.com via a 301 redirect.
First it will query:
SELECT * FROM project where domain = 'coderbased.blogstraps.com'
Since the query returns data indicating that the custom domain has been set, it will trigger a 301 redirect to coderbased.com
At this point, the user can only access the site through coderbased.com. When the user visits coderbased.com and, for example, wants to query the articles, the system will perform a similar process but will use the custom domain instead of the .blogstraps.com suffix:
SELECT a.* FROM article a
INNER JOIN project p on a.project_id = p.id
WHERE p.custom_domain = {host}
That’s all for today. If you have a system design topic you're interested in, or any other interesting system design solution for specific topic, don't hesitate to let me know—I’d be more than happy to share them here.
Propose Topic For Next Post
Propose Your Solution