Use compose.dev.yaml and compose.prod.yaml with fixed Go cache mounts, block sudo make dev, build Air outside app/tmp for rootless Docker, soften English spam checks, and simplify contact error copy. Co-authored-by: Cursor <cursoragent@cursor.com>
Technical Kiwi Limited — Website
A small business site built with Go, templ (server-side HTML templates), and HTMX for gallery interactions.
Requirements
- Docker and Docker Compose
Quick start
cp .env.example .env # optional SMTP settings
make dev # live reload → http://localhost:7331
Make targets
| Target | Description |
|---|---|
make dev |
Dev with Air + templ browser live reload (default) |
make build |
Build production website image |
make up |
Run production website service |
make down |
Stop dev and prod compose stacks |
make generate |
Run templ generate in dev container |
make tidy |
Run go mod tidy in dev container |
make logs |
Follow dev container logs |
make sync-media |
Copy photos and convert videos from upload/ → app/images/ |
make thumbs |
Generate gallery thumbnails and video posters under app/images/ |
make publish |
sync-media then thumbs |
Features
- Single-page layout: hero, services, gallery, contact
- Gallery loads via HTMX (
hx-get="/gallery") - Lightbox modal with previous/next navigation
- Contact form with SMTP relay (HTMX submit, no page reload)
- Serves gallery media from
app/images/(published copy; not in git) - Password-protected gallery admin at
/admin/
Gallery admin
Set both ADMIN_USER and ADMIN_PASSWORD in .env, then open http://localhost:8080/admin/ (or via your public URL).
- Sign in with username/password
- Upload JPEGs into an album (or create a new album folder)
- Delete images (removes originals and generated thumbs/hero)
- Changes appear on the public site immediately
Contact form (SMTP)
Set relay details in .env:
| Variable | Description |
|---|---|
SMTP_HOST |
Relay hostname (required) |
SMTP_PORT |
Port (default 587) |
SMTP_USER / SMTP_PASSWORD |
Auth if your relay requires it |
SMTP_FROM |
Envelope/header From address (required) |
SMTP_TO |
Where contact messages are delivered (required) |
SMTP_TLS |
auto, tls (465), or plain (25/local relay) |
If SMTP is not configured, the page shows a mailto fallback instead of the form.
Dev container
make dev uses compose.dev.yaml: mounts ./app and ./upload (raw media staging) plus your host Go caches (~/go/pkg/mod, ~/.cache/go-build by default). Override with GOMODCACHE / GOCACHE in .env. Prefer make dev without sudo so those paths resolve correctly.
Use http://localhost:7331 in the browser — the templ proxy injects live reload on .templ, .go, and .css changes. Port 8080 hits the app directly without auto-reload.
Only run dev or website at a time if you map both to the same host ports.
make up uses compose.prod.yaml: production website mounts ./app/images into the container. Uncomment the Caddy network/labels in that file when deploying behind your reverse proxy.
Project layout
app/
cmd/server/ HTTP server entrypoint
internal/gallery/ Scan and sort images from disk
internal/contact/ Form validation
internal/mail/ SMTP relay
internal/handlers/ Routes and HTMX partials
templates/ templ components (.templ → generated Go)
static/ CSS
images/ Published gallery (served at /images/)
upload/ Raw media — run `make sync-media` to publish into app/images/
Dockerfile Production image
Dockerfile.dev Dev image (Go + templ + Air)
compose.dev.yaml
compose.prod.yaml
.env.example
Gallery media
- Drop photos and videos into repo-root
upload/(album folders, same layout as the gallery). - Run
make sync-media— JPEGs are copied; videos are converted to H.264 MP4 inapp/images/. - Run
make thumbs(ormake publishfor both steps) — buildsthumbs/andhero/derivatives underapp/images/.
The site serves files from app/images/ at /images/…. Thumbnails and hero images are gitignored and rebuilt when sources are newer.
Deploy notes
After adding media, run make publish on the host before or after deploy. First request may take a moment while any missing thumbnails are generated.