Stop Rebuilding Your Deploy Setup From Scratch
tags: #ansible #nginx #vps #systemd #devopsHere’s a scenario I lived through more times than I’d like to admit.
New project. Excited to build the thing. Sit down to deploy it, and suddenly it’s two days later and I’m deep in SSH configs, Nginx rewrites, and trying to remember how I got Certbot working last time. The code was done. The ceremony wasn’t.
The worst part? I’d done it all before. Just not in a way I could repeat.
Every server was a snowflake. Different directory layouts, different Nginx configs written from memory with slightly different conventions each time, systemd service files that worked until they didn’t. Nothing documented well enough to hand off, or even to hand back to myself six months later.
If you’ve shipped more than one project to a VPS, you’ve probably felt this. The first deploy of anything is always a little painful. That’s fine. What shouldn’t happen is the second deploy being just as painful because you never captured what you learned the first time.
The thing I actually needed
I didn’t need a platform. I didn’t need Heroku or Railway or Render. I’ve used those, and there are good reasons to, but I wanted to own my stack. What I needed was a system: a repeatable, codified way to take any project and deploy it to my VPS without starting from scratch each time.
So I built one. I call it acleron-platform.
The core idea is a deployment contract. Each project carries an infra/project.yml file that says what it needs:
Project repo: "Here is what this app needs."
Platform repo: "Here is how this VPS satisfies those needs."
The platform is Ansible-based. It SSHs into the server and makes it match the state described in that file. Static frontend? Nginx serves it. Node backend? systemd runs it and watches it. TLS? Certbot handles it once, and Ansible owns the config from there on out. Secrets? Vault file outside the repo, never committed.
When I start a new project now, I copy a template project.yml, fill in the values specific to that project, and run one command:
mise run deploy
That’s it. The same roles, the same Nginx template, the same systemd unit template. Applied to a new project in minutes instead of days.
The two guides
If you want to set this up for yourself, I wrote it all down. There are two docs because there are two distinct moments:
How to bootstrap a fresh Ubuntu VPS for Ansible deployments: start here if you have a new server. This covers getting the VPS ready: admin user, firewall, SSH hardening, Node.js, pnpm, and the bootstrap script that does it all in one shot.
How to set up repeatable VPS project infrastructure with Ansible, Nginx, and systemd: once your server is ready, this covers the deployment system itself: the project contract, Ansible roles, Nginx routing, systemd services, TLS, vault secrets, and the local developer workflow that ties it together.
Read them in that order if you’re starting from zero. If your VPS is already set up, skip straight to the second one.
Is this for you?
Probably not if you’re shipping one project and happy with a managed platform. The overhead of learning Ansible to deploy a single app isn’t worth it.
But if you’re like me, building multiple projects, wanting to own the infrastructure, tired of rebuilding the same setup every time, the upfront investment pays off fast. The third project you deploy takes about ten minutes. That’s the version of this I wish I’d had three years ago.
The platform repo and the example project scaffold are on GitHub if you want to take a look before committing to anything. Drop me a message and I’ll share access.