Table of Contents
- Traefik Redis Provider Integration - Implementation Summary
- Overview
- Problem Statement
- Solution
- Changes Made
- 1. Configuration (pages.go)
- 2. New Function: registerTraefikRouter (pages.go)
- 3. Modified: registerCustomDomain (pages.go)
- 4. New Method: SetWithTTL (cache.go)
- 5. Comprehensive Tests
- 6. Documentation (README.md)
- Technical Details
- Testing
- Files Modified
- Benefits
- Usage
- Compatibility
- Security Considerations
Traefik Redis Provider Integration - Implementation Summary
Overview
Implemented Redis-based dynamic router configuration for Traefik in the pages-server plugin. This feature enables automatic SSL certificate provisioning for custom domains by writing Traefik router configurations to Redis, which Traefik's Redis provider can dynamically load.
Problem Statement
Previously, custom domains were registered in Redis cache, but Traefik couldn't automatically request SSL certificates for them because it didn't know about these domains until a request arrived. This required manual router configuration for each custom domain.
Solution
When a custom domain is registered (via the registerCustomDomain function), the plugin now:
- Stores the custom domain mapping in cache (existing behavior)
- Writes Traefik router configuration to Redis (new feature)
- Traefik's Redis provider reads the configuration and requests SSL certificates automatically
Changes Made
1. Configuration (pages.go)
Added four new configuration fields to the Config struct:
TraefikRedisRouterEnabled(bool, default: true) - Enable/disable router registrationTraefikRedisCertResolver(string, default: "letsencrypt-http") - Certificate resolver to useTraefikRedisRouterTTL(int, default: 600) - TTL for router configurations in secondsTraefikRedisRootKey(string, default: "traefik") - Redis root key for Traefik config
Updated CreateConfig() to set sensible defaults for all new fields.
2. New Function: registerTraefikRouter (pages.go)
Created a new function that writes Traefik router configuration to Redis:
func (ps *PagesServer) registerTraefikRouter(ctx context.Context, customDomain string) error
Features:
- Returns early if
TraefikRedisRouterEnabledis false - Only works with RedisCache (gracefully skips for MemoryCache)
- Sanitizes domain names for router names (replaces dots with dashes)
- Writes 6 configuration keys per domain:
- rule: Host rule for the domain
- entryPoints/0: "websecure" (HTTPS)
- middlewares/0: "pages-server@file"
- service: "noop@internal"
- tls/certResolver: Configured cert resolver
- priority: "10"
- Uses configurable TTL for router configs
- Returns detailed errors for debugging
3. Modified: registerCustomDomain (pages.go)
Updated to call registerTraefikRouter after registering the domain mapping:
if err := ps.registerTraefikRouter(ctx, pagesConfig.CustomDomain); err != nil {
// Log error but don't fail the request
fmt.Printf("Warning: failed to register Traefik router for %s: %v\n", pagesConfig.CustomDomain, err)
}
Error handling is non-blocking - if router registration fails, the custom domain still works, just without automatic SSL.
4. New Method: SetWithTTL (cache.go)
Added SetWithTTL method to RedisCache to support custom TTLs:
func (rc *RedisCache) SetWithTTL(key string, value []byte, ttlSeconds int) error
Features:
- Allows storing values with different TTLs than the cache's default
- Falls back to in-memory cache if Redis is unavailable
- Returns errors for proper error handling
- Refactored existing
Setmethod to useSetWithTTLinternally
5. Comprehensive Tests
Added extensive test coverage in custom_domain_test.go:
- TestTraefikRouterConfigDefaults - Verifies default configuration values
- TestRegisterTraefikRouterDisabled - Tests disabled router registration
- TestRegisterTraefikRouterWithMemoryCache - Tests graceful skip with in-memory cache
- TestRegisterTraefikRouterWithRedis - Tests full router registration flow
- TestRegisterTraefikRouterSanitizesRouterName - Tests domain name sanitization
- TestRegisterTraefikRouterCustomRootKey - Tests custom Redis root keys
- TestRegisterTraefikRouterCustomCertResolver - Tests custom cert resolvers
Added tests for SetWithTTL in cache_test.go:
- TestRedisCacheSetWithTTL - Tests basic TTL functionality
- TestRedisCacheSetWithTTLDifferentFromDefault - Tests custom TTL vs default
- TestRedisCacheSetWithTTLFallback - Tests fallback to in-memory cache
6. Documentation (README.md)
Added comprehensive documentation:
- Updated configuration reference table with new parameters
- New section: "Traefik Redis Provider Integration"
- Explains how the feature works
- Provides complete configuration examples
- Documents router registration format
- Lists benefits and fallback behavior
- Includes disable/enable instructions
Technical Details
Router Configuration Format
For each custom domain, the plugin writes these Redis keys:
traefik/http/routers/custom-{domain}/rule = "Host(`example.com`)"
traefik/http/routers/custom-{domain}/entryPoints/0 = "websecure"
traefik/http/routers/custom-{domain}/middlewares/0 = "pages-server"
traefik/http/routers/custom-{domain}/service = "noop@internal"
traefik/http/routers/custom-{domain}/tls/certResolver = "letsencrypt-http"
traefik/http/routers/custom-{domain}/priority = "10"
Where {domain} is sanitized (dots → dashes), e.g., example-com.
Error Handling
- Non-blocking: Router registration failures don't prevent custom domain from working
- Graceful fallback: Automatically skips registration when using in-memory cache
- Logging: Errors are logged with context for debugging
- Resilient: Uses existing Redis connection pool and fallback mechanisms
Performance Considerations
- Minimal overhead: Only writes 6 small keys per custom domain
- Connection pooling: Reuses existing Redis connections
- TTL management: Router configs expire automatically (default: 600s)
- Cached: Domain mappings remain cached even if router registration fails
Testing
All tests pass successfully:
- Configuration defaults: ✓
- Disabled state handling: ✓
- Memory cache graceful skip: ✓
- SetWithTTL fallback: ✓
Redis-dependent tests require a running Redis/Valkey instance at localhost:6379.
Files Modified
-
/Users/richardharvey/git/code/pages-server/pages.go- Added configuration fields
- Added
registerTraefikRouterfunction - Modified
registerCustomDomainfunction
-
/Users/richardharvey/git/code/pages-server/cache.go- Added
SetWithTTLmethod - Refactored
Setto useSetWithTTL
- Added
-
/Users/richardharvey/git/code/pages-server/custom_domain_test.go- Added 7 new test functions
- Added
fmtimport
-
/Users/richardharvey/git/code/pages-server/cache_test.go- Added 3 new test functions for
SetWithTTL
- Added 3 new test functions for
-
/Users/richardharvey/git/code/pages-server/README.md- Added configuration parameters to reference table
- Added "Traefik Redis Provider Integration" section
Benefits
- Zero Configuration: No manual router creation for each custom domain
- Automatic SSL: Traefik automatically requests SSL certificates
- Dynamic Discovery: Traefik discovers new custom domains automatically
- Scalable: Works with unlimited custom domains
- Backward Compatible: Disabled by default for non-Redis setups
- Resilient: Graceful degradation if Redis is unavailable
Usage
To enable this feature:
- Configure Traefik's Redis provider in static config
- Set
redisHostin plugin configuration - Optionally customize
traefikRedisCertResolver,traefikRedisRouterTTL, etc. - Visit pages URL to register custom domain
- Traefik automatically handles SSL certificate provisioning (routers use
noop@internalservice)
To disable:
- Set
traefikRedisRouterEnabled: falsein plugin configuration
Compatibility
- Standard library only (Yaegi compatible)
- Works with existing Redis implementation
- No breaking changes to existing functionality
- Graceful degradation for non-Redis deployments
Security Considerations
- Uses existing Redis authentication
- No new credentials or secrets required
- TTL-based expiration prevents stale configurations
- Non-blocking error handling prevents DoS via misconfiguration