Files
landing/DEPLOYMENT.md
2026-01-30 17:39:41 -03:00

8.5 KiB
Raw Blame History

Deployment Guide

Woodpecker builds the site, pushes the image to Giteas container registry, then triggers a Portainer stack redeploy via webhook. The stack on your Linode VPS pulls the new image and recreates the container.

Portainer stack options

You can run the stack in Portainer in two ways; both use the pre-built image (Option A).

Option Description When to use
Git repository Portainer pulls the stack definition from this repo (docker-compose.yml). Compose path: docker-compose.yml. On webhook: Portainer pulls latest compose + image and redeploys. Stack definition (ports, env) lives in git; one repo for app + stack.
Web editor You paste the compose YAML in Portainer. No compose file in the repo. On webhook: Portainer pulls the image and redeploys. You prefer to manage stack only in Portainer and keep the repo app-only.

This repo includes docker-compose.yml for the Repository option. The compose file only references the image (git.mifi.dev/mifi-ventures/landing:latest); it does not build. In Portainer, enable Re-pull image so each webhook pulls the new :latest and recreates the stack.

Prerequisites

  • Gitea with Woodpecker CI and container registry enabled
  • Linode VPS with Docker; Portainer installed and managing that environment
  • Portainer stack created (Repository pointing at this repo, or Web editor with equivalent compose)

Step-by-Step Setup

1. Prepare VPS and Portainer

  • Install Docker on the Linode VPS and run Portainer (e.g. as a container or on the host).
  • In Portainer, add your Gitea registry: Settings → Registries → Add registry. Use the same URL and credentials you use for REGISTRY_* in Woodpecker so the host can pull git.mifi.dev/mifi-ventures/landing:latest.

2. Create the Portainer stack

If using Git repository (recommended):

  1. Stacks → Add stack → name (e.g. landing).
  2. Choose Git repository.
  3. Repository URL: https://git.mifi.dev/mifi-ventures/landing.git (or your clone URL). Add credentials if the repo is private.
  4. Repository reference: main.
  5. Compose path: docker-compose.yml.
  6. Enable GitOps updatesWebhook. Copy the webhook URL for the Woodpecker secret portainer_webhook_url.
  7. Enable Re-pull image so each webhook pulls the new image.
  8. Optionally set stack env vars: LANDING_PORT=8080 (or leave default in compose).
  9. Deploy the stack.

If using Web editor:

  1. Stacks → Add stack → name (e.g. landing).
  2. Choose Web editor and paste the same structure as docker-compose.yml (service with image: git.mifi.dev/mifi-ventures/landing:latest, pull_policy: always, ports, restart).
  3. Enable the stack webhook, copy the URL for portainer_webhook_url.
  4. Deploy the stack.

3. Gitea container registry

Use Giteas built-in container registry. Image path:

  • Registry URL: git.mifi.dev
  • Image (REGISTRY_REPO): git.mifi.dev/mifi-ventures/landing
  • Create a Gitea token or use your password with package:write (or equivalent) for the mifi-ventures/landing package. Use that as registry_password in Woodpecker.

4. Configure Woodpecker Secrets

In Gitea → Repository → Settings → Secrets (Woodpecker):

Name Value
registry_password Gitea token or password (package write to the repos registry)
portainer_webhook_url Portainer stack webhook URL (from step 2)

5. Configure Woodpecker Variables

In Gitea → Repository → Settings → Variables (Woodpecker):

Name Value
REGISTRY_URL git.mifi.dev
REGISTRY_REPO git.mifi.dev/mifi-ventures/landing
REGISTRY_USERNAME Your Gitea username

6. Test Deployment

Local Test Build

# Clone repo
git clone https://git.mifi.dev/mifi-ventures/landing.git
cd 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

# 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 (Gitea → Repository → Pipelines).
# After build + push, deploy step calls Portainer webhook; Portainer redeploys the stack.

7. Verify Deployment

After the pipeline completes:

  • In Portainer, open the stack and confirm the service is running and the image was pulled.
  • From your machine: curl http://your-vps-ip:8080 (or the port you set in the stack).
  • In a browser: visit http://your-vps-ip:8080 (or your domain if you use a reverse proxy).

Port Configuration

The stack compose uses LANDING_PORT (default 8080). Set it in Portainer stack env vars or in docker-compose.yml:

  • Direct port 80: set LANDING_PORT=80 in the stack.
  • Custom port (e.g. behind Traefik): set LANDING_PORT=8080 and proxy 80/443 → 8080.

Note: With Traefik, security headers are added at the proxy level.

Traefik Integration Example

If using Traefik as reverse proxy, add labels to the service in docker-compose.yml:

services:
  landing:
    image: ${LANDING_IMAGE:-git.mifi.dev/mifi-ventures/landing:latest}
    pull_policy: always
    restart: unless-stopped
    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"
    networks:
      - traefik-public

networks:
  traefik-public:
    external: true

(Remove or omit ports if Traefik is the only entry point.)

Common Issues

Registry Login Failed (Woodpecker push)

  • Ensure REGISTRY_URL is git.mifi.dev and REGISTRY_REPO is git.mifi.dev/mifi-ventures/landing.
  • Use a Gitea token with package write permission (or your password if allowed).
  • Test locally: echo "TOKEN" | docker login git.mifi.dev -u USERNAME --password-stdin.

Portainer Cannot Pull Image

  • In Portainer, add the Gitea registry under Settings → Registries with the same URL and credentials.
  • Ensure the stacks Re-pull image (or equivalent) is enabled so redeploys pull the new image.

Webhook Returns 4xx/5xx

  • Confirm the webhook URL is correct and the stack exists.
  • In Portainer, open the stack → Webhook and verify the URL matches the secret portainer_webhook_url.

Container Wont Start (Portainer)

  • In Portainer, open the stack → service → Logs.
  • Check for port conflicts on the host (e.g. another service using the same port).
  • Ensure the Gitea registry is added in Portainer so the image can be pulled.

Rollback Procedure

If a bad image was deployed:

  1. Portainer: Open the stack → Redeploy with “Re-pull image” off, or edit the stack to use a specific image tag (e.g. git.mifi.dev/mifi-ventures/landing:<commit-sha>) if you tag by SHA in Woodpecker.
  2. Or in Gitea, revert the commit and push to main; the pipeline will build and push a new image, then the webhook will redeploy. Ensure the reverted commit builds successfully.

Monitoring

  • Portainer: Stack → service → Logs, Inspect, Stats.
  • On the host (if you have SSH): docker ps, docker logs <container>, docker stats <container>.
  • Nginx logs (from host): docker exec <container> tail -f /var/log/nginx/access.log (and error.log).
  • Disk: Portainer → Host → disk usage; or on host: docker system df, docker image prune -af --filter "until=72h".

Security Checklist

  • Registry (Gitea) uses authentication; token stored only in Woodpecker secrets
  • Portainer webhook URL stored only in Woodpecker secrets (not in repo)
  • VPS firewall configured (ufw or iptables)
  • Traefik (or reverse proxy) handles TLS termination if used
  • Container runs as non-root user (nginx user in the image)
  • Regular security updates on the host (apt update && apt upgrade)

Next Steps

  1. Set up monitoring (Prometheus + Grafana)
  2. Configure automated backups
  3. Add Slack/Discord notifications to the Woodpecker pipeline
  4. Set up a staging stack in Portainer (e.g. different port or branch)
  5. Optionally tag images by commit SHA in Woodpecker for easier rollback in Portainer

Last Updated: 2026-01-30
Maintainer: Mike Fitzpatrick