356 lines
8.4 KiB
Markdown
356 lines
8.4 KiB
Markdown
# Deployment Guide
|
|
|
|
Quick reference for setting up Woodpecker CI/CD deployment.
|
|
|
|
## Prerequisites
|
|
|
|
- Gitea instance with Woodpecker CI configured
|
|
- Private Docker registry (Docker Hub, GitHub Container Registry, Harbor, etc.)
|
|
- Linode VPS with Docker installed
|
|
- SSH access to VPS
|
|
|
|
## Step-by-Step Setup
|
|
|
|
### 1. Prepare VPS
|
|
|
|
SSH into your Linode VPS and install Docker:
|
|
|
|
```bash
|
|
# Update system
|
|
sudo apt update && sudo apt upgrade -y
|
|
|
|
# Install Docker
|
|
curl -fsSL https://get.docker.com -o get-docker.sh
|
|
sudo sh get-docker.sh
|
|
|
|
# Create deploy user (optional, recommended)
|
|
sudo useradd -m -s /bin/bash deploy
|
|
sudo usermod -aG docker deploy
|
|
|
|
# Verify Docker works
|
|
docker --version
|
|
docker ps
|
|
```
|
|
|
|
### 2. Generate SSH Key for Deployment
|
|
|
|
On your local machine or CI server:
|
|
|
|
```bash
|
|
# Generate SSH key pair
|
|
ssh-keygen -t ed25519 -C "woodpecker-deploy" -f ~/.ssh/woodpecker_deploy
|
|
|
|
# Copy public key to VPS
|
|
ssh-copy-id -i ~/.ssh/woodpecker_deploy.pub deploy@your-vps-ip
|
|
|
|
# Test connection
|
|
ssh -i ~/.ssh/woodpecker_deploy deploy@your-vps-ip "echo 'Connection successful'"
|
|
|
|
# Get private key content (copy this to Woodpecker secret)
|
|
cat ~/.ssh/woodpecker_deploy
|
|
```
|
|
|
|
### 3. Configure Docker Registry
|
|
|
|
**Option A: Docker Hub**
|
|
```bash
|
|
REGISTRY_URL=docker.io
|
|
REGISTRY_REPO=docker.io/yourusername/mifi-ventures-landing
|
|
REGISTRY_USERNAME=yourusername
|
|
# Password: Use Docker Hub access token (not your account password)
|
|
# Generate at: https://hub.docker.com/settings/security
|
|
```
|
|
|
|
**Option B: GitHub Container Registry**
|
|
```bash
|
|
REGISTRY_URL=ghcr.io
|
|
REGISTRY_REPO=ghcr.io/yourusername/mifi-ventures-landing
|
|
REGISTRY_USERNAME=yourusername
|
|
# Password: GitHub Personal Access Token with write:packages scope
|
|
# Generate at: https://github.com/settings/tokens
|
|
```
|
|
|
|
**Option C: Self-hosted Registry**
|
|
```bash
|
|
REGISTRY_URL=registry.example.com
|
|
REGISTRY_REPO=registry.example.com/mifi-ventures-landing
|
|
REGISTRY_USERNAME=yourusername
|
|
# Password: Your registry password or token
|
|
```
|
|
|
|
### 4. Configure Woodpecker Secrets
|
|
|
|
Navigate to: `Gitea → Repository → Settings → Secrets`
|
|
|
|
Add these secrets (click "Add secret" for each):
|
|
|
|
| Name | Value | Notes |
|
|
|------|-------|-------|
|
|
| `registry_password` | Your registry password/token | From step 3 |
|
|
| `deploy_host` | `123.45.67.89` | Your Linode VPS IP |
|
|
| `deploy_username` | `deploy` | SSH user from step 1 |
|
|
| `deploy_ssh_key` | `-----BEGIN OPENSSH...` | Private key from step 2 |
|
|
| `deploy_port` | `22` | Default SSH port |
|
|
|
|
**Important**: For `deploy_ssh_key`, paste the entire private key including:
|
|
```
|
|
-----BEGIN OPENSSH PRIVATE KEY-----
|
|
...entire key content...
|
|
-----END OPENSSH PRIVATE KEY-----
|
|
```
|
|
|
|
### 5. Configure Woodpecker Variables
|
|
|
|
Navigate to: `Gitea → Repository → Settings → Variables`
|
|
|
|
Add these variables:
|
|
|
|
| Name | Value | Example |
|
|
|------|-------|---------|
|
|
| `REGISTRY_URL` | Registry base URL | `ghcr.io` |
|
|
| `REGISTRY_REPO` | Full image path | `ghcr.io/username/mifi-ventures-landing` |
|
|
| `REGISTRY_USERNAME` | Registry username | `yourusername` |
|
|
| `CONTAINER_NAME` | Container name | `mifi-ventures-landing` |
|
|
| `APP_PORT` | Host port | `8080` |
|
|
|
|
### 6. Test Deployment
|
|
|
|
#### Local Test Build
|
|
```bash
|
|
# Clone repo
|
|
git clone https://gitea.example.com/you/mifi-ventures-landing.git
|
|
cd mifi-ventures-landing
|
|
|
|
# Build locally
|
|
docker build -t test .
|
|
|
|
# Run locally
|
|
docker run -d -p 8080:80 --name test test
|
|
|
|
# Test
|
|
curl http://localhost:8080
|
|
|
|
# Cleanup
|
|
docker stop test && docker rm test
|
|
```
|
|
|
|
#### Trigger CI/CD
|
|
```bash
|
|
# Make a small change
|
|
echo "# Test deployment" >> README.md
|
|
|
|
# Commit and push to main
|
|
git add README.md
|
|
git commit -m "test: trigger deployment"
|
|
git push origin main
|
|
|
|
# Watch pipeline in Woodpecker UI
|
|
# Check: Gitea → Repository → Pipelines
|
|
```
|
|
|
|
### 7. Verify Deployment
|
|
|
|
After pipeline completes:
|
|
|
|
```bash
|
|
# Check container is running
|
|
ssh deploy@your-vps-ip "docker ps | grep mifi-ventures-landing"
|
|
|
|
# Check container logs
|
|
ssh deploy@your-vps-ip "docker logs mifi-ventures-landing"
|
|
|
|
# Test HTTP response
|
|
curl http://your-vps-ip:8080
|
|
|
|
# Check from browser
|
|
# Visit: http://your-vps-ip:8080
|
|
```
|
|
|
|
## Port Configuration
|
|
|
|
The pipeline uses `APP_PORT` to expose the container. Choose based on your setup:
|
|
|
|
### Option 1: Direct Port 80 (No Reverse Proxy)
|
|
```bash
|
|
APP_PORT=80
|
|
# Container listens on port 80 directly
|
|
# Access at: http://your-vps-ip
|
|
```
|
|
|
|
### Option 2: Custom Port (With Traefik/Nginx)
|
|
```bash
|
|
APP_PORT=8080
|
|
# Container listens on 8080
|
|
# Traefik/Nginx proxies from 80/443 → 8080
|
|
# Access via domain: https://mifi.ventures
|
|
```
|
|
|
|
**Note**: With Traefik, security headers are added at the proxy level.
|
|
|
|
## Traefik Integration Example
|
|
|
|
If using Traefik as reverse proxy, add labels to container:
|
|
|
|
```yaml
|
|
# In docker-compose.yml or docker run command
|
|
labels:
|
|
- "traefik.enable=true"
|
|
- "traefik.http.routers.mifi.rule=Host(`mifi.ventures`)"
|
|
- "traefik.http.routers.mifi.entrypoints=websecure"
|
|
- "traefik.http.routers.mifi.tls.certresolver=letsencrypt"
|
|
- "traefik.http.services.mifi.loadbalancer.server.port=80"
|
|
```
|
|
|
|
Update Woodpecker deploy script to include labels:
|
|
```bash
|
|
docker run -d \
|
|
--name $CONTAINER_NAME \
|
|
--restart unless-stopped \
|
|
--label "traefik.enable=true" \
|
|
--label "traefik.http.routers.mifi.rule=Host(\`mifi.ventures\`)" \
|
|
--label "traefik.http.routers.mifi.entrypoints=websecure" \
|
|
--label "traefik.http.routers.mifi.tls.certresolver=letsencrypt" \
|
|
--label "traefik.http.services.mifi.loadbalancer.server.port=80" \
|
|
--network traefik-public \
|
|
$REGISTRY_REPO:latest
|
|
```
|
|
|
|
## Common Issues
|
|
|
|
### SSH Permission Denied
|
|
```bash
|
|
# Check key permissions (must be 600)
|
|
chmod 600 ~/.ssh/woodpecker_deploy
|
|
|
|
# Verify public key is on server
|
|
ssh deploy@host "cat ~/.ssh/authorized_keys"
|
|
|
|
# Test with verbose output
|
|
ssh -vvv -i ~/.ssh/woodpecker_deploy deploy@host
|
|
```
|
|
|
|
### Registry Login Failed
|
|
```bash
|
|
# Test login locally
|
|
echo "PASSWORD" | docker login registry.example.com -u username --password-stdin
|
|
|
|
# For GitHub: Ensure token has write:packages scope
|
|
# For Docker Hub: Use access token, not password
|
|
```
|
|
|
|
### Container Won't Start
|
|
```bash
|
|
# Check logs
|
|
ssh deploy@host "docker logs mifi-ventures-landing"
|
|
|
|
# Check for port conflicts
|
|
ssh deploy@host "netstat -tulpn | grep :80"
|
|
|
|
# Verify image pulled successfully
|
|
ssh deploy@host "docker images | grep mifi-ventures"
|
|
```
|
|
|
|
### Health Check Fails
|
|
```bash
|
|
# Check nginx is running
|
|
ssh deploy@host "docker exec mifi-ventures-landing ps aux"
|
|
|
|
# Test internally
|
|
ssh deploy@host "docker exec mifi-ventures-landing wget -O- http://localhost/"
|
|
|
|
# Check if firewall blocking
|
|
ssh deploy@host "ufw status"
|
|
```
|
|
|
|
## Rollback Procedure
|
|
|
|
If deployment fails:
|
|
|
|
```bash
|
|
# SSH to VPS
|
|
ssh deploy@your-vps-ip
|
|
|
|
# List available images
|
|
docker images | grep mifi-ventures
|
|
|
|
# Find previous working SHA
|
|
# (from Gitea commit history or Docker labels)
|
|
|
|
# Stop current container
|
|
docker stop mifi-ventures-landing
|
|
docker rm mifi-ventures-landing
|
|
|
|
# Start previous version
|
|
docker run -d \
|
|
--name mifi-ventures-landing \
|
|
--restart unless-stopped \
|
|
-p 8080:80 \
|
|
registry.example.com/mifi-ventures-landing:PREVIOUS_SHA
|
|
|
|
# Verify
|
|
docker ps | grep mifi-ventures-landing
|
|
curl http://localhost:8080
|
|
```
|
|
|
|
## Monitoring
|
|
|
|
### Container Status
|
|
```bash
|
|
# Check if running
|
|
docker ps | grep mifi-ventures-landing
|
|
|
|
# View logs (last 100 lines)
|
|
docker logs --tail 100 mifi-ventures-landing
|
|
|
|
# Follow logs in real-time
|
|
docker logs -f mifi-ventures-landing
|
|
|
|
# Check resource usage
|
|
docker stats mifi-ventures-landing
|
|
```
|
|
|
|
### Nginx Access Logs
|
|
```bash
|
|
# View access logs
|
|
docker exec mifi-ventures-landing tail -f /var/log/nginx/access.log
|
|
|
|
# View error logs
|
|
docker exec mifi-ventures-landing tail -f /var/log/nginx/error.log
|
|
```
|
|
|
|
### Disk Space
|
|
```bash
|
|
# Check Docker disk usage
|
|
docker system df
|
|
|
|
# Cleanup old images (automatic in pipeline, or manual)
|
|
docker image prune -af --filter "until=72h"
|
|
|
|
# Full cleanup (careful!)
|
|
docker system prune -a --volumes
|
|
```
|
|
|
|
## Security Checklist
|
|
|
|
- [ ] SSH key is Ed25519 (not RSA)
|
|
- [ ] Private key is stored securely in Woodpecker (never in repo)
|
|
- [ ] Deploy user has minimal permissions (not root if possible)
|
|
- [ ] Registry uses authentication (not public)
|
|
- [ ] VPS firewall configured (ufw or iptables)
|
|
- [ ] Traefik handles TLS termination (if used)
|
|
- [ ] Container runs as non-root user (nginx user)
|
|
- [ ] Regular security updates (`apt update && apt upgrade`)
|
|
|
|
## Next Steps
|
|
|
|
1. Set up monitoring (Prometheus + Grafana)
|
|
2. Configure automated backups
|
|
3. Add Slack/Discord notifications to pipeline
|
|
4. Set up staging environment
|
|
5. Implement blue-green deployments for zero downtime
|
|
|
|
---
|
|
|
|
**Last Updated**: 2026-01-29
|
|
**Maintainer**: Mike Fitzpatrick
|