Deploying a side project on a single cheap VPS is the best way to master infrastructure. Learn to secure, automate, and manage your stack without breaking.

Deploying a side project on a single cheap VPS is a rite of passage for every developer who wants to move beyond the "it works on my machine" phase. When you're paying $5 a month for a box with 1GB of RAM, you learn to appreciate efficiency and hate bloat.
I’ve spent years moving apps from expensive managed platforms back to bare-metal VPS instances. It’s not just about the money; it’s about understanding exactly what happens when a request hits your server.
My first mistake was trying to run a full microservices architecture on a tiny droplet. I had a database, a cache, and three separate containers for my API. Within two hours, the OOM (Out of Memory) killer was working overtime. The system spent more time swapping to disk than executing code.
I learned the hard way that when you're working with limited resources, simplicity is your best feature. Forget Kubernetes. Forget complex service meshes. If you're building a side project, stick to a single-server setup using Docker and a reverse proxy. It’s fast, portable, and keeps your memory footprint predictable.
For a standard stack (Node.js, Python, or Go with a PostgreSQL backend), I usually stick to Ubuntu 22.04 LTS. It’s boring, stable, and has the best community support when things go sideways.
Before you even git clone your repo, harden the box:
npm install or a heavy database migration.Using Docker on a single cheap VPS makes your deployments atomic. Instead of manually installing dependencies, you define your environment in a Dockerfile.
Dockerfile# A simple multi-stage build to keep the image small FROM node:20-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm install COPY . . RUN npm run build FROM node:20-alpine WORKDIR /app COPY --from=builder /app/dist ./dist COPY --from=builder /app/node_modules ./node_modules CMD ["node", "dist/main.js"]
By using multi-stage builds, you keep your production image lean. I’ve seen images drop from 800MB to under 150MB just by switching to Alpine Linux. That saves disk space and reduces your attack surface.
You don't need a fancy CI/CD pipeline to get started. I currently use a simple GitHub Action that triggers an SSH command to my VPS. It pulls the latest image, runs docker compose up -d --build, and cleans up old images.
If your project starts getting traffic, you'll eventually face the same issues I did with Laravel Horizon graceful shutdowns. If you're running background workers, make sure your Docker containers handle SIGTERM signals correctly. Otherwise, you'll lose jobs every time you deploy an update.
When you're restricted to one server, you have to be smarter about your resource allocation. Don't let your database and application fight for CPU cycles.
One thing I'm still figuring out is logging. On a single VPS, logs can fill up your disk in a few weeks if you aren't careful. I’ve had instances go down because /var/log consumed all available space. Now, I always set max-size in my Docker logging configuration:
YAML# docker-compose.yml services: app: image: my-app logging: driver: "json-file" options: max-size: "10m" max-file: "3"
This ensures that even if your app goes rogue and logs like crazy, it won't crash the entire server.
Q: Should I use a managed database or host it on the VPS? A: If it's a side project, start with the database on the VPS to save money. Just ensure you have a cron job backing up your data to S3 or an R2 bucket.
Q: How do I handle downtime during deployments?
A: Use a blue-green deployment strategy or a simple docker-compose update. For most side projects, a 5-second window of downtime during a restart is perfectly acceptable.
Q: Is 1GB of RAM really enough? A: Yes, if you optimize your stack. Avoid memory-hungry runtimes if you don't need them. If you're running a heavy Node.js app, consider moving to Go or Rust for the high-concurrency parts if you hit a wall.
I’m still experimenting with automated backups. I used to rely on manual exports, but I've recently started using automated scripts that ship database snapshots to cold storage. It's not perfect, and I’m sure I’ll break something eventually, but it’s a massive step up from having no recovery plan at all. Don't over-engineer it—just make sure you can get back online if the server provider decides to reboot your instance.
Linux server hardening doesn't have to be manual. Learn to use Lynis for automated security audits and fail2ban to block brute-force attacks effectively.