Files
landing/README.md
2026-02-13 14:50:12 -03:00

6.1 KiB
Raw Blame History

mifi.holdings — Landing Page

Static landing site for mifi.holdings (and www). Plain HTML/CSS, no framework; served by nginx in Docker behind Traefik, with CI/CD via Woodpecker and deployment via Portainer.


Quick reference

What Where
Site mifi.holdings, www.mifi.holdings (HTTPS)
Runtime nginx (Alpine) in Docker
Reverse proxy / TLS Traefik (Let's Encrypt)
CI/CD Woodpecker (ci → build → deploy)
Registry git.mifi.dev/mifi-holdings/landing
Package manager pnpm

Architecture

                    ┌─────────────────┐
                    │   Traefik       │  (routing, TLS, websecure)
                    └────────┬────────┘
                             │
                    ┌────────▼────────┐
                    │  Docker container │  mifi-holdings-landing
                    │  nginx:alpine    │  port 80
                    │  /usr/share/    │
                    │  nginx/html ←   │  static files from image
                    └─────────────────┘
  • Build: Docker image = nginx:alpine + project nginx/conf.d/ + src/ copied into /usr/share/nginx/html.
  • Run: Single service on external network marina-net; Traefik routes mifi.holdings and www.mifi.holdings to this container (HTTPS, security-prison@file middleware).

Repo structure

.
├── src/                    # Static site (served as-is)
│   ├── index.html
│   └── css/
│       └── style.css
├── nginx/
│   └── conf.d/
│       └── default.conf    # nginx server config (cache rules, SPA fallback)
├── .woodpecker/
│   ├── ci.yml              # Lint + format check (PR + push to main)
│   ├── build.yml           # Build image, push to registry (main)
│   └── deploy.yml          # Trigger Portainer redeploy (main)
├── docker-compose.yml      # Service + Traefik labels for production
├── Dockerfile              # nginx + config + src
├── package.json            # pnpm scripts: format, lint, docker build/push
└── README.md

Tech stack

  • Frontend: Static HTML + CSS only (no JS build step).
  • Server: nginx (Alpine) in Docker.
  • Tooling: pnpm (format with Prettier, lint with yamllint for .woodpecker/*.yml and docker-compose.yml).
  • Deployment: Docker image → Gitea registry → Portainer stack redeploy.

Local development

  • Dependencies: pnpm install (only devDependencies: Prettier, yaml-lint).
  • Format: pnpm format / pnpm format:check.
  • Lint: pnpm lint (yamllint on Woodpecker and docker-compose YAML).
  • Run locally (Docker):
    • Build: pnpm docker:build (or docker build --platform linux/amd64 -t git.mifi.dev/mifi-holdings/landing:latest .).
    • Run: use docker-compose up only on a host that has marina-net and Traefik; otherwise run the image with a port map and open http://localhost:<port>.

There is no dev server in this repo; edit src/ and refresh the browser or rebuild the image to test.


Docker

  • Dockerfile: Copies nginx/conf.d/ and src/ into an nginx:alpine image. No multi-stage build; the image is the final runtime.
  • Image: Tagged as git.mifi.dev/mifi-holdings/landing:latest (and :<commit-sha> in CI).
  • Local build/push: pnpm docker:build, pnpm docker:push (requires login to git.mifi.dev).

CI/CD (Woodpecker)

Three pipelines:

  1. ci (.woodpecker/ci.yml)

    • When: Every PR and every push to main.
    • Steps: Install deps → Prettier format check → yamllint (Woodpecker + docker-compose). Mattermost notifications on success/failure.
  2. build (.woodpecker/build.yml)

    • When: Push/tag/manual on main (and deployment to production); depends_on: ci.
    • Steps: Build Docker image (linux/amd64, up to 3 retries) → push :latest and :<CI_COMMIT_SHA> to git.mifi.dev/mifi-holdings/landing. Mattermost notifications.
  3. deploy (.woodpecker/deploy.yml)

    • When: Same as build (main / production deploy); depends_on: ci.
    • Steps: Call Portainer webhook to redeploy the stack. Mattermost notifications.

Secrets used: gitea_registry_username, gitea_package_token, portainer_webhook_url, mattermost_* (bot token, channel IDs, API URL).


Deployment (production)

  • Orchestration: Stack defined in docker-compose.yml, deployed via Portainer (webhook triggered by Woodpecker deploy pipeline).
  • Network: Container joins external network marina-net (shared with Traefik).
  • Traefik:
    • Hosts: mifi.holdings, www.mifi.holdings.
    • Entrypoint: websecure (HTTPS).
    • TLS: Let's Encrypt (tls.certresolver=letsencrypt).
    • Middleware: security-prison@file.
    • Backend: this service, port 80.
  • Healthcheck: wget --spider -q http://localhost/ every 20s (timeout 3s, 3 retries).

To deploy manually: pull the latest image and redeploy the stack in Portainer, or trigger the Portainer webhook (e.g. same URL as in portainer_webhook_url).


nginx behavior

  • Root: /usr/share/nginx/html (contents of src/).
  • HTML: Cache-Control: public, no-cache so updates are visible quickly.
  • JS/CSS: Long cache, immutable for hashed/versioned assets.
  • Images/fonts: Cached (30d / 1y).
  • SPA-style fallback: / tries $uri, $uri/, then index.html, then 404.

Summary

  • What it is: Static landing for mifi.holdings.
  • How it runs: nginx in Docker, fronted by Traefik on marina-net.
  • How its updated: Push to main → Woodpecker runs ci, build (image + push), deploy (Portainer webhook); Mattermost reports status.