How I host everything for my SaaS side-project on a $6 server
Tags: webdev, nginx, sideprojects, devops
Published: 2023-09-01 15:30:37

Building side projects, attracting users, and earning some extra income are something I love to do after work.

## Background

Since I’m a web developer, many of my ideas have these 4 standard components:

- A marketing website or a landing page
- A static front-end web app
- An API server
- A database

Being able to ship fast is my ultimate goal to validate the ideas.

## The Problems

My initial idea was to use services that could automate the infrastructure and deployment for me. I tried using:

- Static web hosting ([Surge.sh](http://surge.sh/), Firebase) for the landing page and web app
- Google App Engine/Heroku Dyno for the backend and database

These tools appealed to me because I could simply type a command on my local machine and everything would get deployed. 

Well, it _did_ work that way, but when I added more requirements, I often found myself struggling through complex documentation without a clear sense of how they worked.

For example:

- Why couldn't my Node app connect to the database?
- How could I avoid caching the `index.html` file on the browser (and only cache assets)?
- How could I point my Cloudflare DNS to the Node app IP?

Don't get me wrong, these tools are capable of fulfilling my requirements, but with limited time outside of work, I don't want to spend most of my time reading documentation.

## The 1-server with Nginx solution

My idea is, can I install everything on a virtual machine (similar to my laptop), and use Nginx to redirect traffic?

Turns out it’s pretty simpler than I thought.

Here’s the URL mapping of my project:

![URL mapping](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v5jyjje64nmf0q469xys.png)

- `www.codediagram.io/app/` is a static web app, which is a VueJS client-side app. All the assets are served from `/opt/front-end-app`
- `www.codediagram.io/` is the landing page. This is generated HTML that is optimized for the SEO. All the assets are served from `/opt/landing-page`
- `api.codediagram.io` is routed to the back-end NodeJS app runs on port 5003

The reason that the static front-end app and the landing page have the same subdomain, only separated by subpath, is that these URLs are user-facing, and I want them to be consistent, easy to use, and remember.

**This is the Nginx config that does all the work:**

```
server {
    server_name www.codediagram.io;

    location / {
        root /opt/landing-page;
        index index.html;
        try_files $uri $uri/ /index.html;
    }

    location /app {
        root /opt/front-end-app;
        try_files $uri $uri/ =404;
    }
}

server {
    server_name api.codediagram.io;

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_pass http://localhost:5003;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_cache_bypass $http_upgrade;
    }
}
```

- The first server block is for the landing page and the front-end app.
- The second server block is for the Node instance.

To enable SSL, I highly recommend using Certbot and letting it auto-configure your Nginx config.

And that's it, everything is set!

## The half-automated deployment

For the landing page and the front-end app. I wrote a small command to build and copy the build to the server.

```
pnpm build \
  ssh server 'rm -rf /opt/front-end-app' \
  scp -r ./.output/public server:/opt/front-end-app
```

For the back-end, I manually:

- SSH into the server
- Checkout new codes
- Restarts [PM2](https://pm2.keymetrics.io/) instances

## The server specs

I’m using Digital Ocean, the 6-dollar droplet is a 1GB RAM, 1vCPU. This is more than I need for now since not many people are using my product.

You can pick any cloud vendor that you like.

## What I’ve learned

- Setting up this environment is similar to setting up a local development on my laptop, with the exception of Nginx.
- Although the Nginx configuration may seem complex at first, all routing logic is contained in that file, making it easier to understand what will happen.
- Having only one server for everything streamlines many processes.
- I can easily replicate this setup for any side project.

This setup is suitable for my use case, which involves a side project with low usage. However, if your project has high usage, then this is not an ideal setup. 

In such cases, it is recommended to split the components into separate servers with backup and high availability features.

If you're interested, this is the tool that I built: https://www.codediagram.io/

Cheers