Workflow
VPS Deployment Flow for Any Web App
VPS Deployment Flow for Any Web App Deploying an app to a VPS is not about one specific framework. The general flow is almost always the same. The stack can...
VPS Deployment Flow for Any Web App
Deploying an app to a VPS is not about one specific framework.
The general flow is almost always the same.
1. Prepare the server
2. Install Docker
3. Create app Dockerfile
4. Create docker-compose.yml
5. Setup environment variables
6. Run the containers
7. Setup reverse proxy
8. Point domain to VPS
9. Install SSL certificate
10. Add deploy workflowThe stack can be different, but the deployment mindset stays the same.
1. Prepare the VPS
First, connect to the server.
ssh user@your_server_ipUpdate the server packages.
sudo apt update && sudo apt upgrade -yInstall basic tools.
sudo apt install -y curl git ufw nginxThe VPS is just a remote Linux machine.
Your job is to make it able to run your application safely.
2. Install Docker
Docker helps package the application with its runtime.
So instead of installing Node, PHP, Python, or Java directly on the server, the app runs inside a container.
curl -fsSL https://get.docker.com | shAllow your user to run Docker without sudo.
sudo usermod -aG docker $USERThen logout and login again.
Check installation:
docker --version
docker compose version3. Create Application Dockerfile
Every app needs a Dockerfile.
The Dockerfile describes how the app should be built and run.
Generic example:
FROM runtime-image
WORKDIR /app
COPY package-files .
RUN install-dependencies
COPY source-code .
RUN build-command
EXPOSE app-port
CMD ["start-command"]For any framework, the idea is the same:
Install dependencies
Copy source code
Build the app
Run the app4. Create docker-compose.yml
Docker Compose is used to run multiple services together.
For example:
app
database
redis
storageGeneric structure:
services:
app:
build: .
container_name: my_app
restart: unless-stopped
ports:
- "3000:3000"
env_file:
- .env
database:
image: postgres:16-alpine
container_name: my_database
restart: unless-stopped
environment:
POSTGRES_DB: app_db
POSTGRES_USER: app_user
POSTGRES_PASSWORD: app_password
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:The important part is:
Containers should be replaceable.
Data should be persistent.That is why database data uses a volume.
5. Setup Environment Variables
Do not hardcode production config inside the source code.
Use .env.
APP_ENV=production
APP_PORT=3000
DATABASE_URL=postgresql://app_user:app_password@database:5432/app_db
APP_URL=https://yourdomain.comInside Docker Compose, service names can be used as hostnames.
Example:
database
redis
appSo the app can connect to PostgreSQL using:
database:5432Not localhost.
Inside a container, localhost means the container itself.
6. Run the Application
Start the app:
docker compose up -d --buildCheck running containers:
docker psCheck logs:
docker compose logs -f appAt this point, the app should be running on the VPS.
Example:
http://your_server_ip:3000But for production, users should access it through domain and HTTPS.
7. Setup Reverse Proxy
A reverse proxy receives public traffic and forwards it to the container.
Common tools:
Nginx
Caddy
Traefik
ApacheWith Nginx, public traffic goes like this:
User
↓
Domain
↓
Nginx port 80/443
↓
Docker app portExample Nginx config:
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
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 config and reload Nginx:
sudo nginx -t
sudo systemctl reload nginx8. Point Domain to VPS
In your DNS provider, create an A record.
Type: A
Name: @
Value: your_server_ipFor subdomain:
Type: A
Name: app
Value: your_server_ipThen your app can be accessed from:
https://yourdomain.com
https://app.yourdomain.comDNS points the domain to the server.
Nginx decides which app should handle the request.
9. Install SSL Certificate
Use Certbot for free SSL from Let’s Encrypt.
sudo apt install -y certbot python3-certbot-nginxGenerate SSL:
sudo certbot --nginx -d yourdomain.comFor subdomain:
sudo certbot --nginx -d app.yourdomain.comAfter this, Nginx will handle HTTPS.
Check auto-renewal:
sudo certbot renew --dry-run10. Add Deployment Workflow
Manual deployment:
git pull
docker compose up -d --buildBetter deployment:
Push code to GitHub
↓
GitHub Actions builds Docker image
↓
Image pushed to registry
↓
VPS pulls latest image
↓
Docker Compose restarts appThis makes deployment more consistent because the VPS only runs the final image.
Generic production flow:
Build image in CI
Push image to registry
Pull image on server
Restart container11. Basic Server Security
A VPS should not be left open carelessly.
Basic checklist:
Use SSH key
Disable password login
Open only needed ports
Use firewall
Keep server updated
Do not expose database port publiclyExample UFW:
sudo ufw allow OpenSSH
sudo ufw allow 80
sudo ufw allow 443
sudo ufw enableDo not expose database like this in production:
ports:
- "5432:5432"Unless you really know why.
Usually, the database should only be accessible inside the Docker network.
12. Deployment Mental Model
Docker runs the app.
Compose connects the services.
Nginx exposes the app.
DNS points users to the server.
SSL secures the connection.
CI/CD makes deployment repeatable.That is the core VPS deployment flow.
The framework can change, but the deployment architecture stays almost the same.
Untuk versi shorts portfolio, ini yang paling pas:
Deploy Any Web App to VPS
Deploying to a VPS is not tied to one framework.
Whether the app uses Node.js, Laravel, Django, Go, or Spring Boot, the production flow is usually similar.
Code
↓
Docker Image
↓
VPS
↓
Docker Compose
↓
Reverse Proxy
↓
Domain
↓
SSLFirst, prepare the server.
ssh user@server_ip
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl git nginxInstall Docker.
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USERCreate a Dockerfile.
FROM runtime-image
WORKDIR /app
COPY dependency-files .
RUN install-dependencies
COPY source-code .
RUN build-command
EXPOSE app-port
CMD ["start-command"]Create docker-compose.yml.
services:
app:
build: .
restart: unless-stopped
env_file:
- .env
ports:
- "3000:3000"
database:
image: postgres:16-alpine
restart: unless-stopped
volumes:
- db_data:/var/lib/postgresql/data
volumes:
db_data:Run the app.
docker compose up -d --build
docker ps
docker compose logs -f appThen put Nginx in front of the app.
server {
listen 80;
server_name yourdomain.com;
location / {
proxy_pass http://127.0.0.1:3000;
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;
}
}Point the domain to the VPS.
A Record
Name: @
Value: server_ipInstall SSL.
sudo certbot --nginx -d yourdomain.comA simple production deployment flow looks like this:
Build image
Push image to registry
Pull image on VPS
Restart containerThe most important idea:
Your VPS should not depend on one framework.
It should only know how to run containers,
route traffic,
and keep the app alive.That is why Docker, reverse proxy, domain, SSL, and environment variables are the basic building blocks of VPS deployment.
Helpful short? Tap like.
Comments
Join the discussion for this short.
Join the discussion
Sign in first, then write a comment or reply to an existing thread.
Sign in with GitHub to start a new comment. After that, this area will become active for writing.
Your GitHub identity will be used for comment attribution.