my-app
A Next.js application deployed on a self-hosted infrastructure stack.
Stack Overview
Browser
└── Caddy (reverse proxy, TLS termination)
├── my-app.mk360.de → Next.js (Docker, port 3003)
├── git.mk360.de → Gitea (port 3000)
├── coolify.mk360.de → Coolify (port 8000)
├── monitor.mk360.de → Uptime Kuma (10.0.1.7)
└── paperclip.mk360.de → Paperclip (port 3100)
Why Next.js?
Next.js provides server-side rendering, file-based routing, and built-in TypeScript support out of the box. It compiles to a standalone output (output: "standalone") which produces a minimal Docker image (~100 MB) containing only the files needed to run the app — no node_modules bloat in the final image.
Why Coolify?
Coolify is a self-hosted deployment platform (similar to Vercel/Heroku) that manages Docker containers, handles rolling updates, and provides a deployment UI and API. It replaces the need for a managed cloud provider, keeping everything on the mk360.de server.
The CI/CD pipeline works as follows:
git push
└── Gitea webhook → Caddy /deploy/my-app → Coolify API
└── Coolify clones repo, builds Docker image, rolling update
Note: Coolify's native Gitea webhook endpoint requires OAuth source integration. The
/deploy/my-appCaddy route acts as a bridge, injecting the Coolify API bearer token so Gitea's plain webhook can trigger authenticated deploys.
Subdomains
| Subdomain | Service | Backend |
|---|---|---|
my-app.mk360.de |
This Next.js app | 127.0.0.1:3003 |
git.mk360.de |
Gitea — self-hosted Git | 127.0.0.1:3000 |
coolify.mk360.de |
Coolify — deployment platform | 127.0.0.1:8000 |
monitor.mk360.de |
Uptime Kuma — uptime monitoring | 10.0.1.7:80 |
paperclip.mk360.de |
Paperclip | 127.0.0.1:3100 |
All subdomains are served over HTTPS via Caddy with automatic TLS certificates.
Build & Deploy
The app is built using a multi-stage Dockerfile with BuildKit cache mounts:
depsstage — installs npm packages, cached via--mount=type=cache,target=/root/.npmbuilderstage — runsnext build, cached via--mount=type=cache,target=/app/.next/cacherunnerstage — minimal Alpine image, copies only the standalone output
This keeps subsequent deploys fast even though Coolify builds with --no-cache by default,
since BuildKit cache mounts persist independently of the layer cache.