Pengin Open Source Installation Guide

Pengin-Pi-3 ships as a docker-compose package containing every service it needs. If you have Docker and Docker Compose, installation is mostly a matter of routing traffic to the Traefik container correctly.

Running the Django app directly, without containers, is possible and is what most contributors do day to day — but it means installing database client libraries and development headers yourself, and those differ by platform.

Pick your path:


Deployment (docker-compose)

For a fresh Fedora, Debian/Ubuntu (WSL2 included), or macOS box, skip the manual steps below and run the matching installer from the release instead:

sudo ./deploy_fedora.sh          # Fedora / dnf-based
sudo ./deploy_debian_ubuntu.sh   # Debian, Ubuntu, or WSL2 running either
./deploy_mac.sh                  # macOS - do not use sudo, it prompts itself when needed

There's no Windows script — on Windows, install WSL2 with an Ubuntu distro and run deploy_debian_ubuntu.sh inside it.

Each installer installs Docker CE and the Compose v2 plugin from Docker's own repository (not your distro's, which tends to lag), installs and configures Fail2ban with the project's baseline profile, asks where to install (default /opt/pengin-pi-3) and copies the project there, then hands off to deploy.sh to build and start the stack — including asking whether you already have your own Traefik/reverse proxy on this host, or want the bundled docker-compose.traefik.yml standalone option. See Deployment for what each of these steps actually does.

The rest of this section covers the manual steps those scripts automate, useful for advanced setups or unsupported platforms.

Requirements

  • Docker Engine and the Docker Compose plugin
  • A host with ports 80 and 443 reachable from wherever traffic originates
  • A domain name pointed at the host, if you want TLS

1. Get the source

Clone from the code mirror:

git clone https://github.com/Pengin-Open-Source/pengin-pi-3.git
cd pengin-pi-3

2. Configure

Copy the example environment file and edit it:

cp .env.example .env

At minimum you will need to set your domain, database credentials, and the Django secret key. AWS credentials are optional for a first boot — mail and media upload will be inert without them, but nothing else breaks. Every setting is documented in Configuration.

3. Bring the stack up

docker compose up --build -d

Watch it come up:

docker compose logs -f

First boot runs migrations and collects static files. Give it a minute before you expect a response.

4. Create your first superuser

docker compose exec web python manage.py createsuperuser

Log in at /admin/ to confirm the stack is healthy, then follow Quickstart to create your first slug page and role.

What's in the stack

docker-compose.yml itself brings up five services — Traefik is deliberately not one of them:

  • nginx — static file serving and request handling in front of the app
  • web — the Django application under uWSGI
  • postgres — relational data
  • ferretdb — document store for runtime content structures
  • redis — cache and analytics write buffer

Traefik — edge router, TLS termination, certificate issuance — is expected to already be running on the host, attached to an external root_proxy Docker network, the same way it does on the project's own production hosts. If you don't have one, the optional docker-compose.traefik.yml overlay (shipped alongside docker-compose.yml) stands up a minimal Traefik with Let's Encrypt configured instead; the one-command installers above offer this automatically. See Deployment for both paths.

Service-by-service detail lives in Deployment.

Network routing

This is where most first installs go wrong, so it's worth being explicit.

Traefik is the only service that should be exposed. It binds ports 80 and 443 on the host and routes inbound requests to Nginx, which passes them to the Django app. Nothing else in the stack needs a published port.

On a cloud host, that means:

  • Open 80 and 443 in your security group or firewall
  • Point an A record at the host's public IP
  • Set your domain in .env so Traefik's router rules and certificate resolver match the hostname requests actually arrive with

Behind a home router or a NAT gateway, forward 80 and 443 to the host's internal address. Traefik cannot obtain a Let's Encrypt certificate unless port 80 reaches it from the public internet.

If you are running behind an additional proxy — CloudFront, a load balancer, another Nginx — read the client IP section in Analytics before you go live. Each proxy hop rewrites the visitor address, and the platform resolves the real one by inspecting forwarded headers in trust order. Getting that chain wrong means your analytics record your own infrastructure.

Verifying

docker compose ps

All services should report healthy. Then check that the app is answering through the full chain rather than just directly:

curl -I https://yourdomain.example/

A 200 or a redirect to your login page means routing is correct. A Traefik 404 means the router rule doesn't match your hostname. A 502 means Traefik found the route but Nginx or the app isn't up — check docker compose logs web.

More failure modes in Troubleshooting.


Local development (no containers)

If you only want to run the Django app — to develop against it, or to poke at it without Docker — you install the services and libraries yourself.

Platform support, stated honestly: this project is developed on Fedora Linux. The commands below are what we actually run. Debian and Ubuntu differ mainly in package names. macOS and Windows are not tested by the maintainers for local development this way — but the one-command deploy_mac.sh and deploy_debian_ubuntu.sh installers above (Docker-based, not this bare-metal path) are tested on both. If you get bare-metal macOS or Windows working cleanly, a pull request to this page would be genuinely useful.

The ordering that matters

Several Python packages compile against system libraries at install time. pip install will fail, sometimes with a confusing compiler error rather than a clear message, if the development headers aren't already present.

So the order is:

  1. Install the database servers you need
  2. Install the development headers and build tools
  3. Create your virtual environment
  4. Then, and only then, pip install

Fedora

Services:

sudo dnf install postgresql-server postgresql redis

Build dependencies and headers:

sudo dnf install gcc python3-devel libpq-devel

Initialize and start PostgreSQL:

sudo postgresql-setup --initdb
sudo systemctl enable --now postgresql redis

FerretDB is not in the Fedora repositories. Run it as a container alongside your local setup, which is what we do:

docker run -d --name ferretdb -p 27017:27017 ghcr.io/ferretdb/ferretdb

Debian and Ubuntu

Same shape, different names:

sudo apt install postgresql redis-server
sudo apt install build-essential python3-dev libpq-dev

macOS

Untested for this bare-metal path. Homebrew provides postgresql, redis, and the libraries; libpq typically needs to be added to your path manually before pip can find it. FerretDB via Docker Desktop. For a working, tested macOS setup, use deploy_mac.sh (the Docker path) instead — see Deployment.

Windows

Untested for this bare-metal path, and the deployment scripts assume a POSIX environment. WSL2 running a supported Linux distribution is the practical route — follow the Fedora or Debian instructions inside WSL and treat it as a Linux install, or just run deploy_debian_ubuntu.sh there.

Python environment

python3 -m venv env
source env/bin/activate
pip install -r requirements.txt

If a package fails to build here, the cause is almost always a missing -devel or -dev package from the step above rather than anything wrong with the requirements file.

Database setup

Create the role and database, then point Django at them in your .env. Exact values are listed in Configuration. Then:

python manage.py migrate
python manage.py createsuperuser
python manage.py runserver

The development server runs on port 8000 by default.

What you lose without containers

Running bare metal gives you the app, not the platform. Fail2ban, the Nginx hardening, TLS, and Traefik routing all live in the deployment layer. Don't benchmark security or performance against a runserver instance — see Security for what the deployed stack actually enforces.


Next