Business OS · Deployment
Business OS is the internal app I run my business from. Each project in it has a Remote tab that creates the server, sets it up, and deploys the code, so I don't have to do it by hand over SSH.
Why I built it
Every new client project meant the same list: create a server, SSH in, install Node and a database, write an nginx config, get a certificate, clone the repo, write a script to pull and restart. Doing it from memory took about an hour, and no two servers ended up quite the same.
Now each project in Business OS holds those details (local folder, repo, server, app path, branch and environment variables), and the setup runs the same way every time.
If the project isn't on GitHub yet, Business OS commits the local folder, creates a private repository through the GitHub API and pushes to main. Once a repo URL is saved on the project, that repo is used from then on instead of a new one being created.
A new server is created through the DigitalOcean API (Sydney region, Ubuntu 24.04, my SSH key attached). The setup waits until SSH actually accepts a connection, then waits for Ubuntu's automatic updates to release the package lock, which is what usually breaks the first install on a fresh server.
There's also an option to put the app on a server I already have. In that case it checks Node, PM2 and nginx are installed and adds the app alongside the others.
Over SSH it installs Node 20, nginx, PM2, PostgreSQL and Redis, and adds swap space. It clones the repo, creates a Postgres database and writes the .env file, then installs dependencies, runs Prisma if the project uses it, and builds. Static sites are served straight from nginx; Node apps run under PM2 behind an nginx proxy. Last, it writes a redeploy.sh script to the server and saves the settings back to the project.
If a domain is set, nginx is updated to use it and Certbot requests a certificate with an HTTP-to-HTTPS redirect. If DNS isn't pointing at the server yet, a small job retries every five minutes and removes itself once the certificate is issued. This step runs separately, so if it fails, the site already running on the IP isn't affected.
Day to day, Push & Deploy commits and pushes the local code, then connects to the server and runs redeploy.sh: pull, install, build, reload PM2. There are separate buttons to only push or only deploy, and the output of each run is saved to the project.
The same tab pushes environment variables to the server or pulls them back, shows and clears PM2 logs, restarts the app, and lets me view and edit the nginx config. The Servers page shows each droplet's CPU, memory and disk from DigitalOcean's monitoring, with health checks and a reboot button.
What it's built on
The main benefit is that every server is set up the same way.
New projects go live faster, and when something needs fixing months later, the server looks like every other one and its settings are saved on the project.