2026-09-30 14:54:38 +03:00
|
|
|
# Running dynamicweb with Docker Compose
|
|
|
|
|
|
2026-10-07 15:33:05 +03:00
|
|
|
Postgres 18, Redis, the web app (Django 6.1, Python 3.12) and a Celery worker in one stack. Only Docker is needed.
|
2026-09-30 14:54:38 +03:00
|
|
|
|
2026-09-30 16:49:20 +03:00
|
|
|
## Quick start (VPS or local)
|
2026-09-30 14:54:38 +03:00
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
git clone <this repo> && cd dynamicweb
|
|
|
|
|
docker compose up -d --build
|
2026-10-09 19:36:30 +03:00
|
|
|
docker compose logs -f web # ready when it prints "Listening at"
|
2026-09-30 14:54:38 +03:00
|
|
|
```
|
|
|
|
|
|
2026-09-30 16:49:20 +03:00
|
|
|
The first start takes a few minutes (image build, migrations, secret key, sample data).
|
2026-10-05 22:46:47 +03:00
|
|
|
`docker compose ps` shows "Up" about 1.5 minutes before the site answers, so wait for the log line.
|
2026-09-30 16:49:20 +03:00
|
|
|
Open port **8000** in the server firewall, then in your browser go to:
|
2026-09-30 14:54:38 +03:00
|
|
|
|
2026-10-01 13:13:49 +03:00
|
|
|
- **VPS:** `http://185.203.114.159:8000/dev-sites/`
|
2026-09-30 16:49:20 +03:00
|
|
|
- **Local machine:** `http://localhost:8000/dev-sites/`
|
2026-09-30 14:54:38 +03:00
|
|
|
|
2026-09-30 16:49:20 +03:00
|
|
|
That page lists all 14 sites as links. Each opens as its own site, so you can keep several
|
|
|
|
|
open in different tabs. (On a VPS the links look like `http://<domain>.<ip>.sslip.io:8000/`;
|
|
|
|
|
sslip.io is public wildcard DNS, so your *browser* needs internet, the server doesn't.)
|
|
|
|
|
|
|
|
|
|
Admin login: `dummy-admin@example.com` / `dummy-admin` at `/en-us/admin/login/`
|
|
|
|
|
(on any site address).
|
|
|
|
|
|
|
|
|
|
## What you will see
|
2026-09-30 14:54:38 +03:00
|
|
|
|
2026-09-30 16:49:20 +03:00
|
|
|
- Every site has sample pages (Home, About, Services, Pricing, Contact) with realistic text.
|
|
|
|
|
- ungleich.ch (and blog, comic), datacenterlight.ch and digitalglarus.ch use their own
|
|
|
|
|
design. **The templates of the other 9 sites are not known**, so they show the plain
|
|
|
|
|
Digital Glarus page. That is a test-data limit, not a bug: in production every site's
|
|
|
|
|
pages and template come from the CMS database.
|
2026-10-05 22:46:47 +03:00
|
|
|
- OpenNebula (VM creation), LDAP accounts and Stripe payments are placeholders and will not
|
|
|
|
|
work (logins use plain Django authentication when no LDAP server is set). Email is printed in the web log.
|
2026-09-30 14:54:38 +03:00
|
|
|
|
2026-09-30 16:49:20 +03:00
|
|
|
## Deploying with your own Postgres (production-like)
|
2026-09-30 14:54:38 +03:00
|
|
|
|
2026-09-30 16:49:20 +03:00
|
|
|
Create `.env` next to `docker-compose.yml`; values there override `.env.docker`:
|
2026-09-30 14:54:38 +03:00
|
|
|
|
|
|
|
|
```
|
2026-10-06 14:31:17 +03:00
|
|
|
POSTGRES_HOST=<host> # PostgreSQL 14 or newer
|
2026-09-30 14:54:38 +03:00
|
|
|
POSTGRES_PORT=5432
|
|
|
|
|
POSTGRES_DB=<db>
|
|
|
|
|
POSTGRES_USER=<user>
|
|
|
|
|
POSTGRES_PASSWORD=<password>
|
2026-09-30 16:49:20 +03:00
|
|
|
SEED_DUMMY_DATA=False
|
|
|
|
|
DEBUG=False
|
|
|
|
|
UNGLEICH_SITE_CONFIGS=<your real JSON>
|
2026-09-30 14:54:38 +03:00
|
|
|
```
|
|
|
|
|
|
2026-09-30 16:49:20 +03:00
|
|
|
plus your real OpenNebula, LDAP, Stripe, email and reCAPTCHA values. Then:
|
2026-09-30 14:54:38 +03:00
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
docker compose up -d --build --no-deps web celery redis
|
|
|
|
|
```
|
|
|
|
|
|
2026-09-30 16:49:20 +03:00
|
|
|
Checklist:
|
|
|
|
|
|
|
|
|
|
- The database already holds the pages, sites and templates (they are stored in the
|
|
|
|
|
CMS; nothing is created for you). Sample data is skipped anyway when it finds page content.
|
|
|
|
|
- `DEBUG=False` loads the production settings and turns off the `/dev-sites/` switcher,
|
|
|
|
|
so reach each site by its real domain name.
|
2026-10-09 19:36:30 +03:00
|
|
|
- The web container runs gunicorn (static files via WhiteNoise); put a TLS-terminating proxy in
|
|
|
|
|
front. Behind one that sets `X-Forwarded-Proto`, set `BEHIND_TLS_PROXY=True`.
|
2026-09-30 16:49:20 +03:00
|
|
|
|
2026-10-09 19:15:41 +03:00
|
|
|
## Image and Kubernetes
|
|
|
|
|
|
2026-10-09 19:36:30 +03:00
|
|
|
One image, `dynamicweb:latest`, serves every role; the `ROLE` variable picks it (compose sets
|
|
|
|
|
`ROLE=celery` on the worker):
|
2026-10-09 19:15:41 +03:00
|
|
|
|
2026-10-09 19:36:30 +03:00
|
|
|
- `web` (default): migrations, then gunicorn on port 8000
|
|
|
|
|
- `celery`: the Celery worker
|
|
|
|
|
- `migrate`: run the migrations and exit
|
2026-10-09 19:15:41 +03:00
|
|
|
|
2026-10-16 14:39:11 +03:00
|
|
|
The entrypoint only picks the role, creates a secret key if `DJANGO_SECRET_KEY` is empty and waits for
|
|
|
|
|
Postgres. The settings read `POSTGRES_*` directly, so you can also override `command:` (for example
|
|
|
|
|
`python manage.py migrate` in an init container); then set `DJANGO_SECRET_KEY` yourself.
|
2026-10-16 17:42:19 +03:00
|
|
|
`migrate` is the project's own: it also copes with a restored old database and creates the sites
|
|
|
|
|
from `UNGLEICH_SITE_CONFIGS`.
|
2026-10-09 19:36:30 +03:00
|
|
|
See [K8S.md](K8S.md) for the Kubernetes deployment.
|
2026-10-09 19:15:41 +03:00
|
|
|
|
2026-09-30 16:49:20 +03:00
|
|
|
## Configuration
|
|
|
|
|
|
|
|
|
|
- `.env.docker`: committed dev defaults. `.env`: your overrides (git-ignored).
|
|
|
|
|
- Add a site: add it to `UNGLEICH_SITE_CONFIGS` (a `Site` row is created on start).
|
2026-10-09 19:36:30 +03:00
|
|
|
- Web server tuning: `GUNICORN_WORKERS` (3), `GUNICORN_TIMEOUT` (120). `RUN_MIGRATIONS=False`
|
|
|
|
|
skips the start-up migrations.
|
2026-09-30 16:49:20 +03:00
|
|
|
- Sample data off: `SEED_DUMMY_DATA=False`. Remove it later:
|
|
|
|
|
`docker compose exec web python manage.py seed_dummy_data --purge`.
|
|
|
|
|
- Restore a real dump: put a `.sql` or `.sql.gz` file in `db-init/`, then
|
|
|
|
|
`docker compose down -v` and start again (dumps are git-ignored).
|
2026-09-30 14:54:38 +03:00
|
|
|
|
2026-10-05 22:46:47 +03:00
|
|
|
## Checking that everything works
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
docker compose exec web python smoke_test.py
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
Opens all sites, their pages and the admin login, and exits non-zero on any failure.
|
|
|
|
|
|
2026-09-30 14:54:38 +03:00
|
|
|
## Useful commands
|
|
|
|
|
|
|
|
|
|
```sh
|
2026-09-30 16:49:20 +03:00
|
|
|
docker compose ps # what is running
|
|
|
|
|
docker compose logs -f web celery # logs
|
|
|
|
|
docker compose up -d --build # after code changes (code is baked into the image)
|
|
|
|
|
docker compose down # stop, keep data (-v wipes it)
|
2026-09-30 14:54:38 +03:00
|
|
|
```
|
2026-10-09 15:51:18 +03:00
|
|
|
|
|
|
|
|
## Troubleshooting
|
|
|
|
|
|
|
|
|
|
- **The build fails at `apk add` with "temporary error (try again later)"**, or hangs there:
|
|
|
|
|
the server's DNS is IPv6-only, which containers on Docker's default network cannot use.
|
|
|
|
|
Give Docker IPv4 resolvers and rebuild:
|
|
|
|
|
|
|
|
|
|
```sh
|
|
|
|
|
echo '{"dns": ["1.1.1.1", "9.9.9.9"]}' > /etc/docker/daemon.json # merge if the file exists
|
|
|
|
|
systemctl restart docker
|
|
|
|
|
docker compose up -d --build
|
|
|
|
|
```
|