- JavaScript 51%
- Nunjucks 34.3%
- Python 14.2%
- Dockerfile 0.5%
| _includes/layouts | ||
| content | ||
| docs | ||
| .dockerignore | ||
| .gitignore | ||
| add_tags.py | ||
| build-watch.js | ||
| build.js | ||
| build.js.backup | ||
| check_links.py | ||
| check_links_v2.py | ||
| dev-server.js | ||
| docker-compose.yml | ||
| Dockerfile | ||
| fix_home_path.py | ||
| fix_home_path2.py | ||
| fix_nav_paths.py | ||
| fix_post_urls.py | ||
| fix_templates.py | ||
| nginx.conf | ||
| package.json | ||
| README.md | ||
alway.swill.be - Complete Architecture Documentation
A self-hosted, Docker-based static photography site with a browser editor. Fast gallery publishing (15×/month), 4 content types, responsive images.
Status: Documentation complete. Ready for implementation.
📋 DOCUMENTATION STRUCTURE
1. ARCHITECTURE.md
High-level tech stack overview. Answers "what?" and "why?"
- Technology choices (11ty, Node.js, Docker, Forgejo, Sharp)
- File organization (folder structure, content types)
- Content formats (YAML galleries, Markdown posts, custom HTML)
- Deployment model (self-hosted, Proxmox homelab)
- Advantages vs. WordPress (speed, control, future-proof)
👉 Start here for context and understanding.
2. BUILD_SCRIPT.md
How the custom Node.js build system works.
- File scanning and content parsing
- Metadata extraction from folder paths
- Template rendering with Nunjucks
- Image processing with Sharp (5 responsive sizes)
- Collections building and sorting
- Complete build flow explanation
👉 Read this to understand how the build works.
3. GALLERY_TEMPLATE_EXAMPLE.md
Real example of gallery structure and rendering.
- Input: YAML post.yaml file with galleries and images
- Output: HTML as it appears on site
- Template: Complete Nunjucks code for rendering galleries
- Demonstrates: Dynamic captions, image paths, layout flexibility
👉 Reference this for gallery structure.
4. LAYOUT_ARCHITECTURE.md
How page layouts are organized and inherited.
- Base layout (site header/footer, common structure)
- Layout inheritance (photoblog.njk wraps base.njk)
- Content-specific layouts (photoblog, text-post, project, page)
- Conditional rendering (show/hide elements per layout)
- Template includes (partials, components)
👉 Reference this when building Nunjucks layouts.
5. DIRECTORY_BASED_LAYOUTS.md
Automatic layout assignment from folder structure.
- Pattern:
directory.jsonper folder type - Eliminates repetition (layout/tags specified once)
- Cascading data (file frontmatter overrides directory.json)
- Examples for photoblog, lofi, writing, projects, pages
👉 Use this pattern for all content folders.
6. EXTRACTED_METADATA.md
Deriving date and other metadata from file paths.
- No
date:field needed in frontmatter - Extract from folder:
content/photoblog/2026-09-17/post.yaml - 11ty filters for date extraction
- Handling multiple posts same day (2026-09-17-2)
- Keeps files clean, DRY principle
👉 Reference for metadata extraction patterns.
7. IMAGE_PROCESSING_SIMPLE.md
Image processing pipeline (Sharp.js, JPEG only).
- Why JPEG only (not WebP): simplicity, universal support
- Processing workflow: 5 responsive sizes (300px → 2400px)
- Quality 85 JPEG (professional photography standard)
- Performance: 2-3 seconds for 300 images (4 workers)
- Nginx caching: 365d TTL for images (immutable)
- File size: ~720KB per image across 5 sizes
👉 Reference for image optimization strategy.
8. CUSTOM_HTML_PAGES.md
Mixed content: YAML galleries + Markdown posts + HTML experiments.
- Three approaches: plain HTML, HTML with layout, Markdown + HTML blocks
- Real examples: before/after slider, equipment list, timeline, comparison tool
- Directory structure: experiments/, tools/, pages/
- When to use each format
👉 Reference when building interactive pages.
9. EDITOR_UI_SPECIFICATION.md
Browser-based gallery editor with live preview.
- Architecture: React/Vue frontend + Node.js/Express backend
- Workflow: upload → arrange → caption → preview → publish
- Features: drag-to-reorder, image management, live YAML preview
- Validation: client-side + server-side
- API endpoints: /api/upload, /api/preview, /api/draft, /api/publish
- Settings page, draft management, mobile responsive
- Deployment: Docker container, TLS/HTTPS
👉 Reference for editor implementation.
10. URL_ROUTING_PATTERNS.md
How folder structure generates URLs.
- Photoblog:
/photoblog/2026-09-17/ - Lofi posts:
/lofi/2026-09-17/ - Writing:
/writing/2026-09-10/ - Projects:
/projects/urbex/(no date) - Static pages:
/pages/about/ - Experiments:
/experiments/before-after-slider/ - Collections sorting (chronological vs. manual)
- Breadcrumbs, tag archives, pagination
- Sitemap and RSS feeds
👉 Reference for URL structure and collections.
11. DEPLOYMENT_WORKFLOW.md
End-to-end: editor publish button → live site.
- Architecture diagram: Editor → Git → Build → Deploy → Nginx
- Editor backend API (/api/publish with validation)
- Git hook that triggers build
- Build script (bash): pulls, builds, deploys
- Build trigger server (Node.js HTTP listener)
- Docker Compose configuration (4 services)
- Nginx reverse proxy + caching
- Monitoring and error recovery
- Timeline: 60 seconds from click to live
👉 Reference for deployment setup.
12. ELEVENTY_CONFIGURATION.md (Reference Only)
Original 11ty configuration kept for reference. Use BUILD_SCRIPT.md instead for the actual build system.
- Shows previous approach with 11ty
- Replaced by custom build.js for more control
- Kept as reference if you want to compare approaches
👉 For reference only; use build.js instead.
🎯 IMPLEMENTATION ROADMAP
Phase 1: Core Infrastructure
- Set up Proxmox VM (2GB RAM, 10GB disk, Ubuntu 22.04)
- Install Docker, Docker Compose, Node.js 18+
- Create folder structure:
/home/data/content/,/home/data/uploads/ - Set up HTTPS with Let's Encrypt
Phase 2: Build System Setup
- Initialize npm project in
/home/data/content/ - Install dependencies:
npm install sharp moment js-yaml nunjucks p-limit chokidar - Copy
build.jsfrom this documentation - Create folder structure:
content/,_includes/,public/ - Copy
build.jsandbuild-watch.jsscripts
Phase 3: Templates & Layouts
- Create base layout:
_includes/layouts/base.njk(header/footer) - Create content layouts:
photoblog.njk,text-post.njk,project.njk,page.njk - Create gallery partial:
_includes/partials/gallery.njk - Create homepage template
- Test with sample post
Phase 4: Editor Application
- Create Express backend:
server/routes/publish.js - Create React/Vue frontend: image upload, drag-to-reorder, live preview
- Implement validation (title length, tags, images per gallery)
- Create authentication (password + optional 2FA)
- Test full workflow: upload → arrange → publish
Phase 5: Build & Deployment
- Build system ready:
build.js(already created) - Set up file watcher (optional):
build-watch.jsfor auto-rebuild - Configure cron (optional): for scheduled builds
- Configure Nginx reverse proxy (see DEPLOYMENT_WORKFLOW.md)
- Set up caching headers (365d images, 1h HTML)
Phase 6: Docker & Production
- Create Dockerfiles for editor, build server
- Write
docker-compose.ymlwith all services - Set up volume mounts for persistence
- Test full stack locally
- Deploy to Proxmox
Phase 7: Testing & Refinement
- Publish test gallery with 20 images
- Verify image processing (all 5 sizes generated)
- Test URL routing (correct paths for all content types)
- Stress test: publish 15 galleries in one day
- Performance tuning (build time, image compression)
Phase 8: Migration (Optional)
- Build WordPress import tool (reads WP database, generates YAML)
- Test import with 5-10 galleries
- Verify image downloads and captions
- Set up Nginx redirects for old WordPress URLs
- Gradually migrate galleries (or all at once)
🚀 QUICK START
Minimal setup for testing:
# 1. Clone/create project
mkdir alway-swill-be
cd alway-swill-be
# 2. Initialize npm
npm init -y
npm install sharp moment js-yaml nunjucks p-limit chokidar
# 3. Create folder structure
mkdir -p content/{photoblog,lofi,writing,projects,pages}
mkdir -p _includes/{layouts,partials}
mkdir -p public
# 4. Copy build system
# Copy build.js from documentation
# Copy build-watch.js for file watcher
# 5. Create sample post
mkdir -p content/photoblog/2026-09-17/images
# Add post.yaml and sample images
# 6. Create sample templates
mkdir -p _includes/layouts
# Add base.njk, photoblog.njk, etc.
# 7. Test build
node build.js
# 8. View output
# Check public/ folder - should see index.html, photoblog/2026-09-17/index.html, etc.
📊 CONTENT TYPES QUICK REFERENCE
| Type | Format | Folder | URL | Date | Rendered By |
|---|---|---|---|---|---|
| Gallery | YAML | photoblog/2026-09-17/ |
/photoblog/2026-09-17/ |
From folder | photoblog.njk |
| Lofi | Markdown | lofi/2026-09-17/ |
/lofi/2026-09-17/ |
From folder | text-post.njk |
| Writing | Markdown | writing/2026-09-10/ |
/writing/2026-09-10/ |
From folder | text-post.njk |
| Project | Markdown | projects/urbex/ |
/projects/urbex/ |
None | project.njk |
| Page | Markdown | pages/about.md |
/pages/about/ |
None | page.njk |
🎨 DESIGN PATTERNS
Gallery structure
title: "Title"
description: "Description"
tags: [tag1, tag2]
galleries:
- slug: gallery-1
title: "Gallery Title"
images:
- filename: image-001.jpg
caption: "Optional caption"
Folder organization
content/
├── photoblog/2026-09-17/
│ ├── post.yaml
│ └── images/
├── lofi/2026-09-17/
│ └── post.md
├── pages/
│ └── about.md
└── experiments/
└── demo.html
Layout hierarchy
base.njk (header/footer/common)
├── photoblog.njk (galleries)
├── text-post.njk (articles)
├── project.njk (projects)
├── page.njk (static)
└── homepage.njk (latest posts)
⚡ PERFORMANCE TARGETS
- Build time: 30-60 seconds (including image processing)
- Image processing: 2-3 seconds for 300 images (4 workers)
- File sizes: ~720KB per image across 5 responsive sizes
- JPEG quality: 85 (imperceptible loss, professional standard)
- Caching: 365 days for images, 1 hour for HTML
- Images per gallery: 1-25
- Publishing frequency: 15 galleries/month
- Total disk usage: ~10GB for one year of content
🔒 SECURITY CONSIDERATIONS
- File validation: Check file types (JPEG/PNG), sizes (max 50MB)
- YAML sanitization: Prevent injection attacks
- Authentication & Encryption: Handled at reverse proxy level (your responsibility)
- File permissions: Editor runs as specific user with limited access
- Path validation: Prevent directory traversal attacks
- Input sanitization: Validate all form inputs before processing
📝 FILE CHECKLIST
Documentation files (these files):
- README.md (this file)
- ARCHITECTURE.md
- GALLERY_TEMPLATE_EXAMPLE.md
- LAYOUT_ARCHITECTURE.md
- DIRECTORY_BASED_LAYOUTS.md
- EXTRACTED_METADATA.md
- IMAGE_PROCESSING_SIMPLE.md
- CUSTOM_HTML_PAGES.md
- EDITOR_UI_SPECIFICATION.md
- URL_ROUTING_PATTERNS.md
- ELEVENTY_CONFIGURATION.md
- DEPLOYMENT_WORKFLOW.md
Configuration files (to create):
.eleventy.jspackage.jsondocker-compose.ymlnginx.conf/home/build/build.sh
Layout templates (to create):
_includes/layouts/base.njk_includes/layouts/photoblog.njk_includes/layouts/text-post.njk_includes/layouts/project.njk_includes/layouts/page.njk_includes/partials/gallery.njk
❓ FAQ
Q: Can I keep WordPress and try this alongside? A: Yes. Set up new site on different domain (staging.alway.swill.be), test thoroughly, then switch DNS.
Q: How do I migrate from WordPress? A: Use the WordPress import tool to read your WP database and generate YAML galleries + download images. See EDITOR_UI_SPECIFICATION.md for details. Set up Nginx redirects for old URLs if needed.
Q: What if I want to edit posts after publishing?
A: Edit post.yaml directly in /content/ folder, then trigger a rebuild. Or add an edit button in the editor UI to modify galleries after publish.
Q: Can I run this on shared hosting? A: No, requires Docker and root access. Self-hosted only (Proxmox, VPS with root, or bare metal).
Q: What happens if build fails? A: Previous version stays live. Editor shows error. Check build logs. Fix issue. Retry publish. See DEPLOYMENT_WORKFLOW.md error recovery.
Q: How do I add a new content type?
A: Create folder (e.g., content/videos/), create directory.json with layout, create layout template, add collection in .eleventy.js. Document it.
Q: How do I version control the content? A: Separately if needed (Git, Forgejo, etc.). The editor writes directly to filesystem without requiring version control. You can add Git separately if you want version history.
Q: Is there a mobile app? A: No, editor is web-based (responsive design). Works on iPad/tablet.
Q: Can I schedule posts? A: Not in current spec. Could add this as v2 feature (scheduled_publish time).
📞 NEXT STEPS
- Read ARCHITECTURE.md for full context
- Choose implementation phase (start with Phase 1-2)
- Copy ELEVENTY_CONFIGURATION.md to
.eleventy.js - Follow DEPLOYMENT_WORKFLOW.md for setup
- Test with sample gallery before going live
- Migrate content gradually or all at once
📚 REFERENCES
- 11ty docs: https://www.11ty.dev/
- Express.js: https://expressjs.com/
- Sharp.js: https://sharp.pixelplumbing.com/
- Forgejo: https://forgejo.org/
- Docker: https://docs.docker.com/
- Nunjucks: https://mozilla.github.io/nunjucks/
📄 LICENSE
This architecture and documentation is for alway.swill.be photography site.
Last updated: September 17, 2026
Status: Documentation Complete ✓
All architectural decisions documented. Ready for implementation whenever you are.