A collection of project templates for different languages and frameworks.
Functionality and structure will vary depending on the template — see the README.md inside each template folder (e.g., go/, python-django/) for specific details.
All templates are built from scratch using patterns that have worked well for me in real-world projects.
Feel free to use, adapt, and extend them — enjoy! 🚀
| Aspect | Go Version | Django Version |
|---|---|---|
| Language | Go | Python |
| Entrypoint | Compiled binary (pk_projName) |
manage.py (CLI runner) |
| DB Handling | SQLC + raw | Django ORM + migrations |
| e2e API Test Runner | *noCRUD (python) | *noCRUD (python) |
| Build style | Binary compilation | Source + pip install |
| Runtime | Debian + single binary | Debian + Python runtime |
I keep the same basic structure for all my applications: the frontend is static and served by CloudFront, the backend runs in containers on a box. The frontend is never containerized and never runs on the server.
Route 53
/ \
domain.com api.domain.com
| |
v v
+------------+ +------------------------+
| CloudFront | | The box (VPS/EC2) |
+------------+ | +------------------+ |
| | | Traefik | |
v | | (TLS, routing) | |
+------------+ | +--------+---------+ |
| S3 bucket | | | |
| static SPA | | v |
| build | | +------------------+ |
+------------+ | | Backend | |
| | Go / Django | |
built on the dev machine, | +------------------+ |
pushed by deploy_frontend.sh | | Prometheus | |
| | Grafana | |
the SPA calls | +------------------+ |
https://api.domain.com --------> +------------------------+
|
v
Postgres (RDS)Two independent deploys, two scripts:
| What | Script | Target |
|---|---|---|
| Static SPA build | deploy_frontend.sh |
S3 + CloudFront invalidation |
| Backend + Traefik + Prometheus + Grafana | deploy.sh |
The box, via scp + compose |
Because the two live on different origins, every API call from the browser is cross-origin.
That means FRONTEND_ORIGIN (in ~/env_setup/<project>/prod_backend.sh) must be set to the
CloudFront origin — it feeds the backend's CORS allowlist. If API calls 403/fail preflight, check
that first.
- Create a frontend with the quasar CLI
- Install the CLI: npm i -g @quasar/cli
cdto/frontendnpm init quasar@latest- use . to keep it directly in the frontend
- Run the frontend locally with its dev server (
npm run dev), pointed athttp://localhost:4500 - Test the local backend stack with build_and_deploy_local.sh / verify_docker_builds.sh
One-time AWS setup (console or CLI, not scripted here):
- Private S3 bucket, no static website hosting — CloudFront reads it via OAC
- CloudFront distribution with that bucket as origin, default root object
index.html - Custom error responses: 403 and 404 →
/index.htmlwith status 200 (SPA history mode) - ACM cert in us-east-1 for the apex, attached to the distribution
- Route 53 A/AAAA alias records for the apex → the distribution
Then set BUCKET and DISTRIBUTION_ID at the top of deploy_frontend.sh and run it. It builds,
syncs with split cache headers (hashed assets immutable, index.html no-cache), and invalidates.
Current process is build the backend container locally (with prod settings) and copy to server.
- Remember to backup RDS and migrate(update db schema)!! Prod DB operations are not handled by
deploy.sh
This is all in deploy.sh.. but here is the general process. Look to the backend readme for deep guidance on its container.
Step 1 - On Dev Machine
docker build -f ./backend/Dockerfile -t backend:latest ./backend
docker save -o backend.tar backend:latest
scp backend.tar user@server:/deploypath/
scp compose_deploy.yaml user@server:/deploypath/Step 2 - On Server
docker load < /deploypath/backend.tar
# Note: Ensure the compose file has the correct reference to the container
#check to see the images
docker images
# Start the containers!
docker compose up -d #detached (run in background, return control to terminal)Routing is split by subdomain, not by path prefix:
domain.comis Route 53 → CloudFront → S3. Traefik never sees it, and the box does not serve it.api.domain.comis Route 53 → the box → Traefik → the backend container.- Because the backend owns its whole subdomain, its routes stay clean (
/someEndpoint) with no/apiprefix to strip and nothing environment-specific baked into the code. - The frontend only needs one build-time variable — the API base URL (
https://api.domain.com). Locally that ishttp://localhost:4500; same path shape either way. - The Traefik dashboard is on
api.domain.com/dashboard(behind basic auth), since the apex now belongs to CloudFront.