I strip it down to a bare minimum.
Some people probably don't realize that you can have a full-featured git-push deployment including a PostgreSQL database and Redis on few hundred lines of simple Bash code. The only exception to keeping it bare is security (proper SSL, SELinux, etc.).
- 1 cheap VM per project (I prefer Digital Ocean at this time) + snapshots
- No CI (run tests locally), no staging until later
- Little bit of Bash to configure everything, no IaC
- Simple systemd services (+ maybe systemd socket activation)
- git-push deploy... bad release? repush previous version
- Automatic system updates and log rotation
- Few auxiliary scripts for Rails console (./railsc.sh), backups (./backup.sh), etc.
- External error and performance monitoring, but keeping raw logs on server (rarely need them)
- Stable CentOS/Rocky Linux with long time support, rootless access
I teach all of that in my book https://deploymentfromscratch.com/ and I basically run my oldest side-project on the book demo which is similar to this. Some people might not like Bash, but it's surprisingly refreshing, and I keep my script flexible and idempotent:
Set everything up after providing IP address and domain name in settings.sh:
$ ./setup.sh
Change the database configuration after changing the config file:
$ ./setup.sh -u postgresql
Deploy a new version:
$ git push production master:master
Later on I would separate the database and have staging, but it's overkill for my projects right now.
The funny thing is that apart from having CI and staging we could have the same setup at 2 early stage startups I worked on...and it would probably last 3-5 years with maybe an in-place server spec upgrade, no kidding. People really like to overdo operations and you know where overdoing it leads to? Mistakes. You maintain K8s and then forget to do something basic security-wise that almost kill the company (seen).