Continuous Deployment for Indie Hackers: A Minimalist Pipeline

Hoàng Ngô Anh Đức· 28/05/2026 02:12
Continuous Deployment for Indie Hackers Cover

Many indie hackers and startup teams spend too much time configuring complex, expensive CD systems (like Kubernetes, GitLab CI, or paid cloud pipelines) when they only need to run a small, fast backend service.

You don't need a heavy infrastructure cluster to achieve reliable, zero-downtime continuous deployment. Let's build a **minimalist, bulletproof CD pipeline** that runs on a basic VPS!

### 1. The Single VPS Infrastructure Layout

For a cost-effective, high-speed stack, place three components on a single Linux machine:
1. **Nginx:** The edge web server handling SSL certificate handshakes (Let's Encrypt) and reverse-proxy routing.
2. **Systemd:** Manages your backend process, ensuring it auto-starts after restarts and stays alive under load.
3. **Git Hooks (or simple GitHub Actions runner):** Listens for code pushes to deploy the code.

### 2. Docker Compose Blue-Green Deployments

To deploy updates without dropping user connections, we can use Docker Compose to spin up parallel containers. Let’s review this sample configuration:

```yaml
version: '3.8'
services:
app-blue:
image: my-app:latest
ports:
- "8080:8080"
restart: always
app-green:
image: my-app:latest
ports:
- "8081:8080"
restart: always
```

When deploying:
1. Check which container is currently inactive (e.g. green).
2. Pull the latest code and start the green container (`docker compose up -d app-green`).
3. Wait for the green container's health check to report successful start.
4. Update Nginx configuration dynamically to point upstreams from port `8080` to `8081`.
5. Reload Nginx (`nginx -s reload`) - this handles connections gracefully with **zero downtime**!
6. Stop the blue container (`docker compose stop app-blue`).

### 3. Automating with a Bash Deployment Script

Let's wrap this into a shell script triggered on code push:

```bash
#!/bin/bash
set -e

echo "Deploying latest build..."
docker build -t my-app:latest .

# Blue-Green swap logic
CURRENT_PORT=$(curl -s http://localhost/api/status | jq .port)

if [ "$CURRENT_PORT" == "8080" ]; then
TARGET_PORT="8081"
TARGET_SERVICE="app-green"
SOURCE_SERVICE="app-blue"
else
TARGET_PORT="8080"
TARGET_SERVICE="app-blue"
SOURCE_SERVICE="app-green"
fi

echo "Starting $TARGET_SERVICE on port $TARGET_PORT"
docker compose up -d $TARGET_SERVICE

# Simple health check loop
sleep 5

echo "Swapping proxy target..."
sudo sed -i "s/proxy_pass http:\/\/127.0.0.1:.*/proxy_pass http:\/\/127.0.0.1:$TARGET_PORT;/g" /etc/nginx/sites-available/app
sudo systemctl reload nginx

echo "Stopping old service $SOURCE_SERVICE"
docker compose stop $SOURCE_SERVICE
echo "Deployment successful!"
```

With under 50 lines of bash script, you have created a robust, zero-downtime deployment flow that costs absolutely nothing!

// Read next

// Reader response

Comments

0 Comments

This article has no comments yet.

Name is limited to 60 characters and comment content to 1000 characters.

Hoàng Ngô Anh Đức

// Author

Hoàng Ngô Anh Đức

Senior Full-Stack Engineer & Software Architect

Tôi là một kỹ sư phần mềm giàu kinh nghiệm chuyên thiết kế và xây dựng các hệ thống web hiện đại, scalable backend sử dụng Go, Vue.js, TypeScript và kiến trúc đám mây Cloud. Đam mê chia sẻ kiến thức kỹ thuật và tối ưu hiệu năng phần mềm.