Quick Start

Get started with Bovine Pages Server in minutes

This resource provides setup instructions for Bovine Pages Server with Traefik to host static sites from Forgejo or Gitea repositories.

Prerequisites

Required components include:

  • Traefik v2.0+ (Installation Guide referenced)
  • Forgejo or Gitea instance running and accessible
  • Docker (for Docker setup)
  • Domain name configured to point to Traefik server
  • Redis or Valkey server for full functionality
  • Basic Traefik configuration understanding

Important: Redis/Valkey Requirement

⚠️ Redis (or Valkey) strongly recommended for production deployments.

While in-memory caching is possible, Redis/Valkey unlocks critical features:

  • ✅ Custom Domain HTTPS - Automatic SSL certificate generation
  • ✅ Persistent Caching - Survives plugin restarts
  • ✅ Shared Cache - Multiple Traefik instances supported
  • ✅ Custom Domain Mappings - Persistent storage of mappings

Without Redis/Valkey:

  • ❌ Custom domains won’t receive automatic HTTPS certificates
  • ❌ Cache lost on plugin restart
  • ❌ Performance significantly degraded
  • ❌ Custom domain mappings won’t persist

Quick Setup:

# Docker
docker run -d --name redis -p 6379:6379 redis:7-alpine

# Or use Valkey (Redis fork)
docker run -d --name valkey -p 6379:6379 valkey/valkey:7-alpine

Architecture Overview

The plugin intercepts requests matching your pages domain pattern (e.g., *.pages.example.com), fetches content from Forgejo repositories via the API, optionally caches it in Redis for performance, and serves it as static websites through Traefik.

graph LR
    A[User Browser] -->|HTTPS Request| B[Traefik Reverse Proxy]
    B -->|Middleware| C[Bovine Pages Server Plugin]
    C -->|Fetch Content| D[Forgejo/Gitea API]
    D -->|Repository Data| C
    C -->|Cache| E[(Redis Cache)]
    C -->|Static Files| B
    B -->|HTTPS Response| A

    style B fill:#37a8db,stroke:#2980b9,stroke-width:2px,color:#fff
    style C fill:#9b59b6,stroke:#8e44ad,stroke-width:2px,color:#fff
    style D fill:#27ae60,stroke:#229954,stroke-width:2px,color:#fff
    style E fill:#e74c3c,stroke:#c0392b,stroke-width:2px,color:#fff

Setup Methods

Two approaches available:

  • Docker Compose Setup (Recommended)
  • Standalone Traefik

Docker Compose Setup

Step 1: Configure Traefik Static Configuration

Create or update traefik.yml:

# traefik.yml
api:
  dashboard: true

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"

certificatesResolvers:
  letsencrypt-http:
    acme:
      email: your-email@example.com
      storage: /letsencrypt/acme.json
      httpChallenge:
        entryPoint: web

experimental:
  plugins:
    pages-server:
      moduleName: github.com/SquareCows/pages-server
      version: v0.0.7

providers:
  docker:
    exposedByDefault: false
  file:
    directory: /etc/traefik/dynamic
    watch: true
  redis:
    endpoints:
      - "redis:6379"
    rootKey: "traefik"

Configuration Notes:

  • experimental.plugins enables the Bovine Pages Server plugin
  • certificatesResolvers configures automatic HTTPS with Let’s Encrypt
  • providers.file loads dynamic configuration from files
  • providers.redis enables dynamic router registration for custom domains

Step 2: Configure Dynamic Configuration

Create dynamic/pages-config.yml:

http:
  middlewares:
    pages-server:
      plugin:
        pages-server:
          pagesDomain: pages.example.com
          forgejoHost: https://git.example.com
          # Optional settings:
          # forgejoToken: your-api-token-here
          # enableRedisCache: true
          # redisAddress: redis:6379
          # redisPassword: your-redis-password
          # authSecretKey: your-secret-key-here
          # authCookieDuration: 3600
          # enableCustomDomains: true
          # customDomainCacheTTL: 600
          # enableCustomDomainDNSVerification: true
          # traefikRedisRouterEnabled: true
          # traefikRedisCertResolver: letsencrypt-http

  routers:
    pages-https:
      rule: "HostRegexp(`{subdomain:[a-z0-9-]+}.pages.example.com`)"
      entryPoints:
        - websecure
      middlewares:
        - pages-server
      service: noop@internal
      tls:
        certResolver: letsencrypt-http

    pages-custom-domains-https:
      rule: "HostRegexp(`{domain:.+}`)"
      entryPoints:
        - websecure
      middlewares:
        - pages-server
      service: noop@internal
      priority: 1
      tls:
        certResolver: letsencrypt-http

    pages-http:
      rule: "HostRegexp(`{subdomain:[a-z0-9-]+}.pages.example.com`) || HostRegexp(`{domain:.+}`)"
      entryPoints:
        - web
      middlewares:
        - pages-server
      service: noop@internal
      priority: 1

Step 3: Docker Compose File

version: '3.8'

services:
  traefik:
    image: traefik:v3.0
    container_name: traefik
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - ./traefik.yml:/etc/traefik/traefik.yml:ro
      - ./dynamic:/etc/traefik/dynamic:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./letsencrypt:/letsencrypt
    environment:
      - TZ=UTC
    networks:
      - web

  redis:
    image: redis:7-alpine
    container_name: redis
    restart: unless-stopped
    command: redis-server --requirepass your-redis-password
    volumes:
      - redis-data:/data
    networks:
      - web

networks:
  web:
    external: true

volumes:
  redis-data:

Step 4: Start Services

# Create Docker network
docker network create web

# Create required directories
mkdir -p dynamic letsencrypt

# Set proper permissions for Let's Encrypt storage
touch letsencrypt/acme.json
chmod 600 letsencrypt/acme.json

# Start Traefik
docker-compose up -d

# Check logs
docker-compose logs -f traefik

Standalone Traefik Setup

Step 1: Add Plugin and Redis Provider to Traefik Configuration

Edit traefik.yml:

experimental:
  plugins:
    pages-server:
      moduleName: github.com/SquareCows/pages-server
      version: v0.0.7

providers:
  file:
    directory: /etc/traefik/dynamic
    watch: true
  redis:
    endpoints:
      - "localhost:6379"
    rootKey: "traefik"

Step 2: Create Middleware Configuration

Create /etc/traefik/dynamic/pages.yml:

http:
  middlewares:
    pages-server:
      plugin:
        pages-server:
          pagesDomain: pages.example.com
          forgejoHost: https://git.example.com
          # Optional: Enable DNS verification for custom domains
          # enableCustomDomainDNSVerification: true

Step 3: Configure Routers

http:
  routers:
    pages-https:
      rule: "HostRegexp(`{subdomain:[a-z0-9-]+}.pages.example.com`)"
      entryPoints:
        - websecure
      middlewares:
        - pages-server
      service: noop@internal
      tls:
        certResolver: letsencrypt-http

Step 4: Restart Traefik

sudo systemctl restart traefik
# Or if running manually
traefik --configFile=/etc/traefik/traefik.yml

Setup Your First Repository

Step 1: Create Repository Structure

your-repository/
├── .pages
└── public/
    ├── index.html
    ├── about.html
    └── assets/
        ├── style.css
        └── logo.png

Step 2: Create .pages File

Basic configuration:

enabled: true

Optional settings:

enabled: true
directory_index: true
custom_domain: www.example.com
password: sha256-hash-here

Step 3: Add Your Static Files

Example public/index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Static Site</title>
</head>
<body>
    <h1>Welcome to My Static Site!</h1>
    <p>Hosted with Bovine Pages Server</p>
</body>
</html>

Step 4: Commit and Push

git add .
git commit -m "Initial pages setup"
git push origin main

Step 5: Access Your Site

Your site is now live at:

https://username.pages.example.com/repository/

Replace username with Forgejo/Gitea username, pages.example.com with your domain, and repository with your repository name.


Verification Steps

1. Check Traefik Dashboard

Verify:

  • ✅ Plugin loaded successfully
  • ✅ Middleware configured
  • ✅ Routers active

2. Test Basic Functionality

curl -I https://username.pages.example.com/repository/
# Should return 200 OK

3. Check Logs

Docker setup:

docker-compose logs -f traefik

Systemd setup:

journalctl -u traefik -f

Troubleshooting

Plugin Not Loading

Solutions:

  1. Verify experimental.plugins in static config
  2. Check Traefik version (requires v2.0+)
  3. Ensure internet connectivity
  4. Check logs: docker-compose logs traefik

404 Errors

Checklist:

  1. ✅ Repository has public/ folder with content
  2. ✅ Repository has .pages file with enabled: true
  3. ✅ Router rules match your domain pattern
  4. ✅ DNS points to Traefik server
  5. forgejoHost is correct in middleware config

SSL Certificate Issues

Solutions:

  1. Check Let’s Encrypt rate limits
  2. Verify port 80 is accessible (for HTTP challenge)
  3. Check acme.json permissions: chmod 600 acme.json
  4. Review certificate resolver configuration

Redis Connection Errors

Solutions:

  1. Verify Redis is running: docker-compose ps redis
  2. Check Redis address in middleware config
  3. Verify Redis password matches
  4. Plugin falls back to in-memory cache if unavailable

Next Steps

  1. 📖 Read the Configuration Guide
  2. 🔒 Setup Password Protection
  3. 🌐 Configure Custom Domains
  4. 📁 Enable Directory Listings
  5. 🧹 Setup Cache Reaper

Support

For issues, questions, or contributions, visit the repository.