Quick Start
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.pluginsenables the Bovine Pages Server plugincertificatesResolversconfigures automatic HTTPS with Let’s Encryptproviders.fileloads dynamic configuration from filesproviders.redisenables 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:
- Verify
experimental.pluginsin static config - Check Traefik version (requires v2.0+)
- Ensure internet connectivity
- Check logs:
docker-compose logs traefik
404 Errors
Checklist:
- ✅ Repository has
public/folder with content - ✅ Repository has
.pagesfile withenabled: true - ✅ Router rules match your domain pattern
- ✅ DNS points to Traefik server
- ✅
forgejoHostis correct in middleware config
SSL Certificate Issues
Solutions:
- Check Let’s Encrypt rate limits
- Verify port 80 is accessible (for HTTP challenge)
- Check
acme.jsonpermissions:chmod 600 acme.json - Review certificate resolver configuration
Redis Connection Errors
Solutions:
- Verify Redis is running:
docker-compose ps redis - Check Redis address in middleware config
- Verify Redis password matches
- Plugin falls back to in-memory cache if unavailable
Next Steps
- 📖 Read the Configuration Guide
- 🔒 Setup Password Protection
- 🌐 Configure Custom Domains
- 📁 Enable Directory Listings
- 🧹 Setup Cache Reaper
Support
For issues, questions, or contributions, visit the repository.