From Vercel to VPS: How I’m Deploying My Smart Garbage Collection App

Café Terrace at Night Painting by Vincent van Gogh
For the longest time, all my app deployments lived comfortably on PaaS (Platform-as-a-Service) platforms—Vercel, Render, Heroku, and the like. They were fast, simple, and beginner-friendly. But recently, I faced a challenge that made me rethink that comfort zone.
While interviewing for a job, I was given a task: build a movie web app using the TMDB API and deploy it on your own server. Not a PaaS. A real VPS. And it had to have CI/CD too.
That one requirement made me realize something: I’d never actually deployed an app to a raw server before.
So as a test run, I decided to take one of my side projects—a smart garbage collection app—and try deploying it on a VPS. The frontend was on Vercel, the backend on Render. I wanted to move both to a single server I could manage. Here's how I did it.
Step 1: Choosing a VPS Provider
I chose DigitalOcean. Why?
I had some free credits, which made experimenting feel less risky.
I didn’t want to jump straight into AWS or GCP. That felt like skipping a step in my learning journey.
I wanted to understand what happens under the hood.
The explosion of AI tools has made this easier than ever. With some good prompts and guidance, you can learn how to set up a VPS in a day. Don’t underestimate what you can learn with the right questions.
Step 2: Backend Setup with Spring Boot
My backend is a Spring Boot app. On my local machine, I built a .jar file like this:
mvn clean package -DskipTestsSkipping tests helps speed up the build during deployment.
Once the .jar It is ready, I transfer it to the VPS:
scp target/my-app.jar root@your-vps-ip:/opt/my-app
Before that, I created the folder and assigned the correct permissions:
sudo mkdir -p /opt/my-app
sudo chown $USER:$USER /opt/my-appStep 3: Environment Variables and Systemd Service
In /opt/my-appI created a .env file with the required environment variables. Then, I set up a systemd service to ensure the app runs reliably:
sudo vim /etc/systemd/system/my-app.service[Unit]
Description=Smart Garbage App Backend
After=network.target[Service]
Type=simple
User=your-user
WorkingDirectory=/opt/my-app
ExecStart=/usr/bin/java -jar my-app.jar
EnvironmentFile=/opt/my-app/.env
Restart=always
RestartSec=10[Install]
WantedBy=multi-user.targetTo start the service:
sudo systemctl daemon-reload
sudo systemctl enable my-app
sudo systemctl start my-app
sudo systemctl status my-app
If everything’s working, your backend is now live
Step 4: Reverse Proxy with Nginx
To expose the backend cleanly to the internet, I added Nginx as a reverse proxy:
sudo apt install nginx
Then I created a config file:
sudo vim /etc/nginx/sites-available/my-appserver {
listen 80;
server_name my-app.com; location / {
proxy_pass http://localhost:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Enable the config:
sudo ln -s /etc/nginx/sites-available/my-app /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Now when you visit http://my-app.com/health, you should see a response from your backend.
Step 5: Securing with HTTPS
To move from HTTP to HTTPS, I used Certbot:
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d my-app.com
sudo certbot renew --dry-runStep 6: Add a Basic Firewall
Security matters. I enabled UFW (Uncomplicated Firewall):
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enableStep 7: Logging
You can view logs for your Spring Boot app:
sudo journalctl -u my-app.service -f💎 Random Nugget
"So, actually ,therefore , the course of wisdom What is really sensible, is to let go is to commit oneself, to give oneself up and that's quite mad So we come to the strange conculsion that in madness lies sanity"— Alan watts