diff --git a/README.md b/README.md index 964a9c1..21f7a67 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ compose/ └── services/ # Utility services ├── homarr/ # Dashboard (home.fig.systems) ├── backrest/ # Backup manager (backup.fig.systems) + ├── static-sites/ # Static websites (Caddy) ├── karakeep/ # Bookmark manager with AI (links.fig.systems) ├── ollama/ # Local LLM server (ollama.fig.systems) ├── vikunja/ # Task management (tasks.fig.systems) @@ -74,9 +75,21 @@ compose/ ## 🌐 Domains -All services are accessible via: -- Primary: `*.fig.systems` -- Secondary: `*.edfig.dev` +Three domains are used with different purposes: + +### fig.systems (Homelab Services) +Primary domain for all self-hosted homelab services: +- `*.fig.systems` - All services listed below + +### edfig.dev (Professional/Public) +Professional and public-facing sites: +- `edfig.dev` / `www.edfig.dev` - Personal website/portfolio +- `blog.edfig.dev` - Technical blog + +### figgy.foo (Experimental/Private) +Testing and experimental services: +- `figgy.foo` - Experimental lab (SSO protected) +- `*.figgy.foo` - Test instances of services ### Service URLs @@ -85,6 +98,10 @@ All services are accessible via: | Traefik Dashboard | traefik.fig.systems | ✅ | | LLDAP | lldap.fig.systems | ✅ | | Tinyauth | auth.fig.systems | ❌ | +| **Static Sites** | | | +| Personal Site | edfig.dev | ❌ | +| Blog | blog.edfig.dev | ❌ | +| Experimental Lab | figgy.foo | ✅ | | **Monitoring** | | | | Grafana (Logs) | logs.fig.systems | ❌* | | Loki (API) | loki.fig.systems | ✅ | @@ -183,6 +200,7 @@ cd compose/media/automation/recyclarr && docker compose up -d cd compose/media/automation/profilarr && docker compose up -d # Utility services +cd compose/services/static-sites && docker compose up -d # Static websites (edfig.dev, blog, figgy.foo) cd compose/services/karakeep && docker compose up -d cd compose/services/ollama && docker compose up -d cd compose/services/vikunja && docker compose up -d diff --git a/compose/core/lldap/compose.yaml b/compose/core/lldap/compose.yaml index 406acab..49899d7 100644 --- a/compose/core/lldap/compose.yaml +++ b/compose/core/lldap/compose.yaml @@ -13,7 +13,7 @@ services: - homelab labels: traefik.enable: true - traefik.http.routers.lldap.rule: Host(`lldap.fig.systems`) || Host(`lldap.edfig.dev`) + traefik.http.routers.lldap.rule: Host(`lldap.fig.systems`) traefik.http.routers.lldap.entrypoints: websecure traefik.http.routers.lldap.tls.certresolver: letsencrypt traefik.http.services.lldap.loadbalancer.server.port: 17170 diff --git a/compose/core/tinyauth/compose.yaml b/compose/core/tinyauth/compose.yaml index 9a0b0a6..6eabc34 100644 --- a/compose/core/tinyauth/compose.yaml +++ b/compose/core/tinyauth/compose.yaml @@ -11,7 +11,7 @@ services: labels: traefik.enable: true # Web UI routing - traefik.http.routers.tinyauth.rule: Host(`auth.fig.systems`) || Host(`auth.edfig.dev`) + traefik.http.routers.tinyauth.rule: Host(`auth.fig.systems`) traefik.http.routers.tinyauth.entrypoints: websecure traefik.http.routers.tinyauth.tls.certresolver: letsencrypt traefik.http.routers.tinyauth.service: tinyauth-ui diff --git a/compose/core/traefik/compose.yaml b/compose/core/traefik/compose.yaml index ef75ca5..da28b49 100644 --- a/compose/core/traefik/compose.yaml +++ b/compose/core/traefik/compose.yaml @@ -34,7 +34,7 @@ services: labels: traefik.enable: true # Dashboard routing - traefik.http.routers.traefik.rule: Host(`traefik.fig.systems`) || Host(`traefik.edfig.dev`) + traefik.http.routers.traefik.rule: Host(`traefik.fig.systems`) traefik.http.routers.traefik.entrypoints: websecure traefik.http.routers.traefik.tls.certresolver: letsencrypt traefik.http.routers.traefik.service: api@internal diff --git a/compose/media/automation/profilarr/compose.yaml b/compose/media/automation/profilarr/compose.yaml index 86c53a7..013b836 100644 --- a/compose/media/automation/profilarr/compose.yaml +++ b/compose/media/automation/profilarr/compose.yaml @@ -22,7 +22,7 @@ services: traefik.docker.network: homelab # Web UI - traefik.http.routers.profilarr.rule: Host(`profilarr.fig.systems`) || Host(`profilarr.edfig.dev`) + traefik.http.routers.profilarr.rule: Host(`profilarr.fig.systems`) traefik.http.routers.profilarr.entrypoints: websecure traefik.http.routers.profilarr.tls.certresolver: letsencrypt traefik.http.services.profilarr.loadbalancer.server.port: 6868 diff --git a/compose/media/automation/qbittorrent/compose.yaml b/compose/media/automation/qbittorrent/compose.yaml index 9320737..b4cf349 100644 --- a/compose/media/automation/qbittorrent/compose.yaml +++ b/compose/media/automation/qbittorrent/compose.yaml @@ -20,7 +20,7 @@ services: - homelab labels: traefik.enable: true - traefik.http.routers.qbittorrent.rule: Host(`qbt.fig.systems`) || Host(`qbt.edfig.dev`) + traefik.http.routers.qbittorrent.rule: Host(`qbt.fig.systems`) traefik.http.routers.qbittorrent.entrypoints: websecure traefik.http.routers.qbittorrent.tls.certresolver: letsencrypt traefik.http.services.qbittorrent.loadbalancer.server.port: 8080 diff --git a/compose/media/automation/radarr/compose.yaml b/compose/media/automation/radarr/compose.yaml index 572aeb2..9de458c 100644 --- a/compose/media/automation/radarr/compose.yaml +++ b/compose/media/automation/radarr/compose.yaml @@ -20,7 +20,7 @@ services: - homelab labels: traefik.enable: true - traefik.http.routers.radarr.rule: Host(`radarr.fig.systems`) || Host(`radarr.edfig.dev`) + traefik.http.routers.radarr.rule: Host(`radarr.fig.systems`) traefik.http.routers.radarr.entrypoints: websecure traefik.http.routers.radarr.tls.certresolver: letsencrypt traefik.http.services.radarr.loadbalancer.server.port: 7878 diff --git a/compose/media/automation/sabnzbd/compose.yaml b/compose/media/automation/sabnzbd/compose.yaml index 23315f7..9bf84c3 100644 --- a/compose/media/automation/sabnzbd/compose.yaml +++ b/compose/media/automation/sabnzbd/compose.yaml @@ -17,7 +17,7 @@ services: - homelab labels: traefik.enable: true - traefik.http.routers.sabnzbd.rule: Host(`sabnzbd.fig.systems`) || Host(`sabnzbd.edfig.dev`) + traefik.http.routers.sabnzbd.rule: Host(`sabnzbd.fig.systems`) traefik.http.routers.sabnzbd.entrypoints: websecure traefik.http.routers.sabnzbd.tls.certresolver: letsencrypt traefik.http.services.sabnzbd.loadbalancer.server.port: 8080 diff --git a/compose/media/automation/sonarr/compose.yaml b/compose/media/automation/sonarr/compose.yaml index 3a24076..79b4c42 100644 --- a/compose/media/automation/sonarr/compose.yaml +++ b/compose/media/automation/sonarr/compose.yaml @@ -20,7 +20,7 @@ services: - homelab labels: traefik.enable: true - traefik.http.routers.sonarr.rule: Host(`sonarr.fig.systems`) || Host(`sonarr.edfig.dev`) + traefik.http.routers.sonarr.rule: Host(`sonarr.fig.systems`) traefik.http.routers.sonarr.entrypoints: websecure traefik.http.routers.sonarr.tls.certresolver: letsencrypt traefik.http.services.sonarr.loadbalancer.server.port: 8989 diff --git a/compose/media/frontend/immich/compose.yaml b/compose/media/frontend/immich/compose.yaml index 47096ae..d40d745 100644 --- a/compose/media/frontend/immich/compose.yaml +++ b/compose/media/frontend/immich/compose.yaml @@ -40,7 +40,7 @@ services: labels: traefik.enable: true traefik.docker.network: homelab - traefik.http.routers.immich.rule: Host(`photos.fig.systems`) || Host(`photos.edfig.dev`) + traefik.http.routers.immich.rule: Host(`photos.fig.systems`) traefik.http.routers.immich.entrypoints: websecure traefik.http.routers.immich.tls.certresolver: letsencrypt traefik.http.services.immich.loadbalancer.server.port: 2283 diff --git a/compose/media/frontend/jellyfin/compose.yaml b/compose/media/frontend/jellyfin/compose.yaml index f2cf897..59ec0d8 100644 --- a/compose/media/frontend/jellyfin/compose.yaml +++ b/compose/media/frontend/jellyfin/compose.yaml @@ -25,7 +25,7 @@ services: - homelab labels: traefik.enable: true - traefik.http.routers.jellyfin.rule: Host(`flix.fig.systems`) || Host(`flix.edfig.dev`) + traefik.http.routers.jellyfin.rule: Host(`flix.fig.systems`) traefik.http.routers.jellyfin.entrypoints: websecure traefik.http.routers.jellyfin.tls.certresolver: letsencrypt traefik.http.services.jellyfin.loadbalancer.server.port: 8096 diff --git a/compose/media/frontend/jellyseer/compose.yaml b/compose/media/frontend/jellyseer/compose.yaml index 98c9a8f..bdfb783 100644 --- a/compose/media/frontend/jellyseer/compose.yaml +++ b/compose/media/frontend/jellyseer/compose.yaml @@ -14,7 +14,7 @@ services: - homelab labels: traefik.enable: true - traefik.http.routers.jellyseerr.rule: Host(`requests.fig.systems`) || Host(`requests.edfig.dev`) + traefik.http.routers.jellyseerr.rule: Host(`requests.fig.systems`) traefik.http.routers.jellyseerr.entrypoints: websecure traefik.http.routers.jellyseerr.tls.certresolver: letsencrypt traefik.http.services.jellyseerr.loadbalancer.server.port: 5055 diff --git a/compose/monitoring/logging/compose.yaml b/compose/monitoring/logging/compose.yaml index 7ec0ff8..4933085 100644 --- a/compose/monitoring/logging/compose.yaml +++ b/compose/monitoring/logging/compose.yaml @@ -26,7 +26,7 @@ services: traefik.docker.network: homelab # Loki API - traefik.http.routers.loki.rule: Host(`loki.fig.systems`) || Host(`loki.edfig.dev`) + traefik.http.routers.loki.rule: Host(`loki.fig.systems`) traefik.http.routers.loki.entrypoints: websecure traefik.http.routers.loki.tls.certresolver: letsencrypt traefik.http.services.loki.loadbalancer.server.port: 3100 @@ -95,7 +95,7 @@ services: traefik.docker.network: homelab # Grafana Web UI - traefik.http.routers.grafana.rule: Host(`logs.fig.systems`) || Host(`logs.edfig.dev`) + traefik.http.routers.grafana.rule: Host(`logs.fig.systems`) traefik.http.routers.grafana.entrypoints: websecure traefik.http.routers.grafana.tls.certresolver: letsencrypt traefik.http.services.grafana.loadbalancer.server.port: 3000 diff --git a/compose/monitoring/uptime/compose.yaml b/compose/monitoring/uptime/compose.yaml index 080b0b9..d6cf1f3 100644 --- a/compose/monitoring/uptime/compose.yaml +++ b/compose/monitoring/uptime/compose.yaml @@ -22,7 +22,7 @@ services: traefik.docker.network: homelab # Web UI - traefik.http.routers.uptime-kuma.rule: Host(`status.fig.systems`) || Host(`status.edfig.dev`) + traefik.http.routers.uptime-kuma.rule: Host(`status.fig.systems`) traefik.http.routers.uptime-kuma.entrypoints: websecure traefik.http.routers.uptime-kuma.tls.certresolver: letsencrypt traefik.http.services.uptime-kuma.loadbalancer.server.port: 3001 diff --git a/compose/services/FreshRSS/compose.yaml b/compose/services/FreshRSS/compose.yaml index 44e36ed..5b1fce5 100644 --- a/compose/services/FreshRSS/compose.yaml +++ b/compose/services/FreshRSS/compose.yaml @@ -22,7 +22,7 @@ services: traefik.docker.network: homelab # Web UI - traefik.http.routers.freshrss.rule: Host(`rss.fig.systems`) || Host(`rss.edfig.dev`) + traefik.http.routers.freshrss.rule: Host(`rss.fig.systems`) traefik.http.routers.freshrss.entrypoints: websecure traefik.http.routers.freshrss.tls.certresolver: letsencrypt traefik.http.services.freshrss.loadbalancer.server.port: 80 diff --git a/compose/services/backrest/compose.yaml b/compose/services/backrest/compose.yaml index 090417d..864f823 100644 --- a/compose/services/backrest/compose.yaml +++ b/compose/services/backrest/compose.yaml @@ -21,7 +21,7 @@ services: labels: # Traefik traefik.enable: true - traefik.http.routers.backrest.rule: Host(`backup.fig.systems`) || Host(`backup.edfig.dev`) + traefik.http.routers.backrest.rule: Host(`backup.fig.systems`) traefik.http.routers.backrest.entrypoints: websecure traefik.http.routers.backrest.tls.certresolver: letsencrypt traefik.http.services.backrest.loadbalancer.server.port: 9898 diff --git a/compose/services/booklore/compose.yaml b/compose/services/booklore/compose.yaml index 2f94e79..2ac9420 100644 --- a/compose/services/booklore/compose.yaml +++ b/compose/services/booklore/compose.yaml @@ -22,7 +22,7 @@ services: traefik.docker.network: homelab # Web UI - traefik.http.routers.booklore.rule: Host(`booklore.fig.systems`) || Host(`booklore.edfig.dev`) + traefik.http.routers.booklore.rule: Host(`booklore.fig.systems`) traefik.http.routers.booklore.entrypoints: websecure traefik.http.routers.booklore.tls.certresolver: letsencrypt traefik.http.services.booklore.loadbalancer.server.port: 3000 diff --git a/compose/services/karakeep/compose.yaml b/compose/services/karakeep/compose.yaml index a9a366b..de398c6 100644 --- a/compose/services/karakeep/compose.yaml +++ b/compose/services/karakeep/compose.yaml @@ -28,7 +28,7 @@ services: traefik.docker.network: homelab # Web UI - traefik.http.routers.karakeep.rule: Host(`links.fig.systems`) || Host(`links.edfig.dev`) + traefik.http.routers.karakeep.rule: Host(`links.fig.systems`) traefik.http.routers.karakeep.entrypoints: websecure traefik.http.routers.karakeep.tls.certresolver: letsencrypt traefik.http.services.karakeep.loadbalancer.server.port: 3000 diff --git a/compose/services/microbin/compose.yaml b/compose/services/microbin/compose.yaml index 694c679..0962b70 100644 --- a/compose/services/microbin/compose.yaml +++ b/compose/services/microbin/compose.yaml @@ -22,7 +22,7 @@ services: traefik.docker.network: homelab # Web UI - traefik.http.routers.microbin.rule: Host(`paste.fig.systems`) || Host(`paste.edfig.dev`) + traefik.http.routers.microbin.rule: Host(`paste.fig.systems`) traefik.http.routers.microbin.entrypoints: websecure traefik.http.routers.microbin.tls.certresolver: letsencrypt traefik.http.services.microbin.loadbalancer.server.port: 8080 diff --git a/compose/services/ollama/compose.yaml b/compose/services/ollama/compose.yaml index fccf305..6b7964e 100644 --- a/compose/services/ollama/compose.yaml +++ b/compose/services/ollama/compose.yaml @@ -35,7 +35,7 @@ services: traefik.docker.network: homelab # API endpoint - traefik.http.routers.ollama.rule: Host(`ollama.fig.systems`) || Host(`ollama.edfig.dev`) + traefik.http.routers.ollama.rule: Host(`ollama.fig.systems`) traefik.http.routers.ollama.entrypoints: websecure traefik.http.routers.ollama.tls.certresolver: letsencrypt traefik.http.services.ollama.loadbalancer.server.port: 11434 diff --git a/compose/services/rsshub/compose.yaml b/compose/services/rsshub/compose.yaml index 6876873..b131437 100644 --- a/compose/services/rsshub/compose.yaml +++ b/compose/services/rsshub/compose.yaml @@ -23,7 +23,7 @@ services: traefik.docker.network: homelab # Web UI - traefik.http.routers.rsshub.rule: Host(`rsshub.fig.systems`) || Host(`rsshub.edfig.dev`) + traefik.http.routers.rsshub.rule: Host(`rsshub.fig.systems`) traefik.http.routers.rsshub.entrypoints: websecure traefik.http.routers.rsshub.tls.certresolver: letsencrypt traefik.http.services.rsshub.loadbalancer.server.port: 1200 diff --git a/compose/services/static-sites/.env b/compose/services/static-sites/.env new file mode 100644 index 0000000..a2c2b33 --- /dev/null +++ b/compose/services/static-sites/.env @@ -0,0 +1,7 @@ +# Caddy Static Sites Configuration + +# Timezone +TZ=America/Los_Angeles + +# Optional: Caddy admin API (disabled by default in Caddyfile) +# CADDY_ADMIN=localhost:2019 diff --git a/compose/services/static-sites/.gitignore b/compose/services/static-sites/.gitignore new file mode 100644 index 0000000..f4fc963 --- /dev/null +++ b/compose/services/static-sites/.gitignore @@ -0,0 +1,10 @@ +# Caddy data and config +caddy_data/ +caddy_config/ + +# Keep example sites but ignore actual site content +# (uncomment if you want to version control your sites) +# sites/ + +# Keep .env.example if created +!.env.example diff --git a/compose/services/static-sites/Caddyfile b/compose/services/static-sites/Caddyfile new file mode 100644 index 0000000..0c1104f --- /dev/null +++ b/compose/services/static-sites/Caddyfile @@ -0,0 +1,57 @@ +# Caddyfile - Static Sites Configuration +# Docs: https://caddyserver.com/docs/caddyfile + +# Global options +{ + # Listen on port 80 (Traefik handles SSL) + auto_https off + admin off +} + +# Personal/Professional Site (edfig.dev) +www.edfig.dev, edfig.dev { + root * /srv/edfig.dev + file_server + encode gzip + + # Try files, then index, then 404 + try_files {path} {path}/index.html index.html + + # Cache static assets + @static { + path *.css *.js *.jpg *.jpeg *.png *.gif *.ico *.svg *.woff *.woff2 *.ttf *.eot + } + header @static Cache-Control "public, max-age=604800, immutable" +} + +# Blog (blog.edfig.dev) +blog.edfig.dev { + root * /srv/blog.edfig.dev + file_server + encode gzip + + # Enable templates for dynamic content + templates + + # Markdown files automatically render as HTML + try_files {path} {path}/index.html {path}.md {path}/index.md index.html + + # Cache static assets + @static { + path *.css *.js *.jpg *.jpeg *.png *.gif *.ico *.svg *.woff *.woff2 *.ttf *.eot + } + header @static Cache-Control "public, max-age=604800, immutable" +} + +# Experimental/Private (figgy.foo) +figgy.foo, www.figgy.foo { + root * /srv/figgy.foo + + # Enable directory browsing for experiments + file_server browse + + encode gzip + + # Templates enabled for dynamic pages + templates +} diff --git a/compose/services/static-sites/README.md b/compose/services/static-sites/README.md new file mode 100644 index 0000000..a4831e3 --- /dev/null +++ b/compose/services/static-sites/README.md @@ -0,0 +1,604 @@ +# Caddy Static Sites Server + +Serves static websites for edfig.dev (professional), blog.edfig.dev (blog), and figgy.foo (experimental). + +## Overview + +**Caddy** is a modern web server with automatic HTTPS and simple configuration: + +- ✅ **Static file serving** - HTML, CSS, JavaScript, images +- ✅ **Markdown rendering** - Write `.md` files, served as HTML automatically +- ✅ **Templates** - Dynamic content with Go templates +- ✅ **Directory browsing** - Beautiful file listing (figgy.foo) +- ✅ **Auto-compression** - Gzip for all responses +- ✅ **Zero-downtime reloads** - Config changes apply instantly + +## Domain Strategy + +### edfig.dev (Professional/Public) +- **Purpose**: Personal website, portfolio +- **URL**: https://edfig.dev or https://www.edfig.dev +- **SSO**: No (public site) +- **Content**: `/sites/edfig.dev/` + +### blog.edfig.dev (Blog/Public) +- **Purpose**: Technical blog, articles +- **URL**: https://blog.edfig.dev +- **SSO**: No (public blog) +- **Content**: `/sites/blog.edfig.dev/` +- **Features**: Markdown auto-rendering, templates + +### figgy.foo (Experimental/Private) +- **Purpose**: Testing, development, experiments +- **URL**: https://figgy.foo or https://www.figgy.foo +- **SSO**: Yes (protected by Tinyauth) +- **Content**: `/sites/figgy.foo/` +- **Features**: Directory browsing, templates + +## Quick Start + +### 1. Deploy + +```bash +cd ~/homelab/compose/services/static-sites +docker compose up -d +``` + +### 2. Access Sites + +- **edfig.dev**: https://edfig.dev +- **Blog**: https://blog.edfig.dev +- **Experimental**: https://figgy.foo (requires SSO login) + +### 3. Verify + +```bash +# Check container is running +docker ps | grep caddy-static + +# Check logs +docker logs caddy-static + +# Test sites +curl -I https://edfig.dev +curl -I https://blog.edfig.dev +``` + +## Directory Structure + +``` +static-sites/ +├── compose.yaml # Docker Compose + Traefik labels +├── Caddyfile # Caddy configuration +├── .env # Environment variables +├── .gitignore # Ignored files +├── README.md # This file +└── sites/ # Site content (can be version controlled) + ├── edfig.dev/ + │ ├── index.html + │ ├── assets/ + │ │ ├── css/ + │ │ ├── js/ + │ │ └── images/ + │ └── ... + ├── blog.edfig.dev/ + │ ├── index.html + │ └── posts/ + │ ├── example-post.md # Markdown posts + │ └── ... + └── figgy.foo/ + ├── index.html + └── experiments/ + └── ... +``` + +## Managing Content + +### Adding/Editing HTML + +Simply edit files in the `sites/` directory: + +```bash +# Edit main site +vim sites/edfig.dev/index.html + +# Add new page +echo "
Page generated at: {{.Now.Format "2006-01-02 15:04:05"}}
+``` + +**Example - Include header:** +```html +{{include "header.html"}} +Thoughts on technology, systems, and automation
+Blog posts will appear here. Stay tuned!
+
+ In the meantime, you can write posts as:
+
+ /srv/blog.edfig.dev/posts/my-post.md
+
+
+ Markdown files (.md) will automatically render as HTML! +
+Software Engineer & DevOps Enthusiast
+ ++ Welcome to my personal site. I build scalable systems, automate infrastructure, + and explore the intersection of technology and efficiency. +
+ + + + +