jew.pizza Website ✡️🍕
Here's the code for the website that powers jew.pizza, my personal website. Built using the CLANG! THUD! IT'S A DARN JEW'S PANDA! stack.
I can't imagine why on earth in a million years you'd want to run this code. So, these instructions are mostly for me — in case of sudden amnesia or coming back to this project after a period of neglect.
It's built using the wildly popular and extremely common CLANG! THUD! IT'S A DARN JEW'S PANDA! stack, ie,
- C is for Compose, ie Docker Compose, a multi-container orchestration tool;
- L is for Liquidsoap, a fantastic scripting language for declaratively describing audio streams;
- A is for Alpine Linux, a lightweight Linux distribution perfect for containers, based on musl libc and BusyBox;
- N is for nginx, a web server and reverse proxy using the jonasal/nginx-certbot container as its base (for HTTPS), using embedded Lua;
- G is for Gunicorn to run the web app via Python's Web Server Gateway Interface;
- T is for Tailwind CSS, a utility-first CSS framework;
- H is for huey, a lightweight asynchronous task queue for Python;
- U is for Umami to get insights via web analytics;
- D is for Docker to run all this crap in containers;
- I for Icecast, a streaming media server for listeners to connect (using Karl Heyes's fork);
- T for Twilio, an API to send programmable communications — in this case, text messages (SMS);
- S for S3, ie Amazon S3 Cloud Object Storage, to store large audio files (I use the compatible DigitalOcean Spaces);
- A is for AlpineJS, a lightweight, reactive front-end JavaScript framework;
- D is for Django, a Python back-end web framework;
- A is for Actions, ie GitHub Actions, to continuously and automatically build, test and deploy this code;
- R is for Redis, a data store and message broker;
- N is for Navigo that provides a simple SPA router;
- J is for Jinja templating — like Django's, but less sucky;
- E is for esbuild, a fast JavaScript bundler;
- W is for WaveSurfer.js, a front-end audio player with waveform visualizations;
- S is for Server-Sent Events (SSE), to send realtime messages to the browser;
- P is for PostgresSQL, a SQL database;
- A for autoheal, ie Docker Autoheal, a tool to monitor and restart unhealthy docker containers;
- N is for Nchan, an nginx module managing EventSource (SSE) clients;
- D is for daisyUI, a lightweight UI component framework on top of Tailwind CSS; and
- A is for audiowaveform, The BBC's offline rendering tool for generating waveforms for WaveSurfer.js.
CLANG! THUD! IT'S A DARN JEW'S PANDA! A very well-known acronym in the engineering world, probably. I definitely didn't just make this up as a joke.
Everything runs with Docker and Docker Compose, including nginx. This can be deployed on any Linux machine.
Installing, compiling, running, and maintaining the motley crew of technologies that make up the CLANG! THUD! IT'S A DARN JEW'S PANDA! stack in both prod and dev environments would be an absolute nightmare. With Docker and Docker Compose, that can be done in just a couple of commands. It even works with Docker Desktop on macOS!
To install these on Debian/Ubuntu,
# Install Docker
curl -fsSL https://get.docker.com | sh
# Install latest Compose
sudo mkdir -p /usr/local/lib/docker/cli-plugins/
sudo curl -fsSL -o /usr/local/lib/docker/cli-plugins/docker-compose \
"$(curl -fsSL https://api.github.com/repos/docker/compose/releases/latest | grep browser_download_url | cut -d '"' -f 4 | grep -i "$(uname -s)-$(arch)$")"
sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
Clone, then copy and edit the .env
file, and optionally copy over the
Docker Compose dev overrides.
git clone https://github.com/dtcooper/jewpizza.git
# Edit me, make sure you set SECRET_KEY!
cp .env.sample .env
# Needed for development only (when DEBUG=1)
ln -s docker-compose.dev.yml docker-compose.override.yml
Assuming you set DOMAIN_NAME=local.jew.pizza
for local development, you'll
want to properly point your system's DNS that way. For example, add the following
to /etc/hosts
.
# jew.pizza local development
127.0.0.1 local.jew.pizza
127.0.0.1 analytics.local.jew.pizza umami.local.jew.pizza
127.0.0.1 etc.local.jew.pizza
127.0.0.1 priv.local.jew.pizza
127.0.0.1 radio.local.jew.pizza play.local.jew.pizza listen.local.jew.pizza
127.0.0.1 www.local.jew.pizza
If you set NGINX_USE_LOCAL_CERTIFICATE_AUTHORITY=1
(and you should for
local development), you'll want to install the phony certificate authority's
root certificate located at <project-dir>/local-certificate-authority/caCert.pem
.
It expires every 30 days.
Build and start containers,
make build
docker compose up
The development app
server will run at http://localhost:8000/, or if you've
set a DOMAIN_NAME
and your /etc/hosts
to work properly with it, navigate to
that. For example, https://local.jew.pizza/. You'll need to install the phony
certificate authority's root certificate (see above).
# Django management command
docker compose run --rm app ./manage.py
# Run shell in app container (make shell)
docker compose run --rm app bash
# Pre commit checks + reformatting (not required)
make pre-commit
The entropy daemon haveged is a nice-to-have to provide your system with randomness to speed up SSL certificate generation. On Debian/Ubuntu, it can be installed via the following,
sudo apt-get install haveged
Pull (or build) containers and run docker compose up
in daemon mode (-d
).
Set all appropriate variables in the .env
file, making note to properly
configure the following,
- An SMTP server — works with SendGrid
- Twilio account SID and auth token
- DigitalOcean Spaces, along with an API key, taking note to run the domain name's DNS off of DigitalOcean. This is necessary for wildcard certificates from Certbot/Lets Encrypt.
docker compose pull
# Or optionally build the containers via: make build
docker compose up -d
Make sure to change these insecure passwords not sent in the .env
file,
- Django:
dave:cooper
- Umami:
dave:cooper
You can automatically deploy the code one of two ways via GitHub Actions,
- Include the string
[deploy]
(or🚀
) in your HEAD commit message and push; or - Trigger the Deploy Workflow manually.
This project is licensed under the MIT License — see the LICENSE file for details.
...and remember kids, have fun!