Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# SimHub configuration template.
#
# Copy this file to ".env" next to compose.yaml and edit as needed:
#
# cp .env.example .env
#
# Docker Compose reads a sibling ".env" automatically, so any values set here
# are picked up by `docker compose up -d` with no extra flags. The real ".env"
# is gitignored -- never commit it.
#
# NOTE ON PASSWORDS: SimHub has no admin-password environment variable. It seeds
# a provisional admin account (admin@simhub.local / admin123) that you are
# forced to re-password in the browser at first sign-in. There is no secret to
# put in this file for that -- set it in the app after the first `up -d`.

# Runtime environment. Leave as "production" for real deployments.
NODE_ENV=production

# Set to "1" (or the number of proxy hops) ONLY when running behind a reverse
# proxy (nginx, Caddy, Traefik) so login rate-limiting sees real client IPs.
# Leave unset otherwise -- trusting proxy headers without a proxy lets clients
# spoof their IP.
# TRUST_PROXY=1

# Port SimHub listens on INSIDE the container. Changing this also requires
# updating the right-hand side of the port mapping in compose.yaml and the
# healthcheck URL, so most deployments leave it at 3000 and change only the
# host (left) side of the compose port mapping instead.
# PORT=3000
23 changes: 18 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,15 +148,27 @@ docker exec simhub node seed.js

### Recommended — Docker Compose

The repository ships a ready-made [docker-compose.yml](docker-compose.yml). Copy it (or clone the repo) onto the server, then:
The repository ships a ready-made [compose.yaml](compose.yaml) that **pulls the published image** — you do **not** need the source checked out on the server. Grab just that one file onto the server and start it:

```bash
docker compose up -d # start
# Download the compose file next to where your data volume will live
curl -fsSL -o compose.yaml \
https://raw.githubusercontent.com/authorTom/simhub/main/compose.yaml

# Optional: configuration overrides (proxy, port). Compose auto-reads a sibling .env
curl -fsSL -o .env.example \
https://raw.githubusercontent.com/authorTom/simhub/main/.env.example
cp .env.example .env # then edit .env if you need TRUST_PROXY, etc.

docker compose up -d # start (pulls the image)
docker compose ps # confirm STATUS shows "(healthy)"
docker compose exec simhub node seed.js # optional: load the example scenario
docker compose logs -f simhub # watch the logs
```

Compose gives you a declarative record of your deployment (port, volume, environment) that you can keep in your team's documentation. Uncomment `TRUST_PROXY: "1"` in the file when running behind a reverse proxy.
Then open `http://<server>:3000` and sign in as `admin@simhub.local` / `admin123`; you are forced to set a real password at first sign-in.

Compose gives you a declarative record of your deployment (port, volume, environment) that you can keep in your team's documentation. To run behind a reverse proxy, set `TRUST_PROXY=1` in `.env` (or uncomment it in `compose.yaml`).

### Your data lives in the volume

Expand All @@ -173,7 +185,7 @@ docker run --rm -v simhub-data:/data -v "$PWD":/backup alpine \
docker compose pull && docker compose up -d
```

That's it — the new container starts against the same data volume, and persisted sessions mean your faculty aren't even signed out. To be able to roll back, deploy a pinned tag (`ghcr.io/authortom/simhub:sha-<commit>` or a release version) instead of `latest`, and change the tag in `docker-compose.yml` when you upgrade.
That's it — the new container starts against the same data volume, and persisted sessions mean your faculty aren't even signed out. To be able to roll back, deploy a pinned tag (`ghcr.io/authortom/simhub:sha-<commit>` or a release version) instead of `latest`, and change the tag in `compose.yaml` when you upgrade.

### Building the image yourself

Expand Down Expand Up @@ -246,7 +258,8 @@ simhub/
├── CHANGELOG.md # Full change history
├── CONTRIBUTING.md # Contribution guide
├── Dockerfile # Production container image
├── docker-compose.yml # Departmental deployment recipe
├── compose.yaml # Departmental deployment recipe (pull-based)
├── .env.example # Config template -> copy to .env
├── LICENSE # MIT licence
├── scenario_template.md # ASPiH scenario blueprint (reference)
├── seed.js # Example dataset generator
Expand Down
59 changes: 59 additions & 0 deletions compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# SimHub -- departmental deployment.
#
# docker compose up -d start (pulls the published image)
# docker compose pull && docker compose up -d update to the latest image
# docker compose exec simhub node seed.js load the example scenario
# docker compose ps check status (look for "healthy")
#
# Compose automatically reads a sibling ".env" file, so copy ".env.example"
# to ".env" and edit it to override any of the settings below.
#
# To build from local source instead of pulling the published image, comment
# out the `image:` line and uncomment the `build: .` line. Building needs the
# full source tree checked out next to this file; pulling does not.

services:
simhub:
# Published automatically to GitHub Container Registry on every push to
# main. GHCR forces the owner/repo to lowercase, hence "authortom".
image: ghcr.io/authortom/simhub:latest
# build: .
container_name: simhub

# Restart on failure and on daemon/host reboot, but not if an operator
# deliberately stopped it.
restart: unless-stopped

# PID 1 that forwards signals and reaps zombies, so SIGTERM reaches node
# and the graceful-shutdown handler can flush state on `compose down`.
init: true

ports:
# "host:container" -- change the LEFT number to serve on a different host
# port. Quoted so YAML never reinterprets it (e.g. 22:22 as base-60).
- "3000:3000"

environment:
# Production defaults. Override in .env if needed.
NODE_ENV: production
# TRUST_PROXY: "1" # set when behind a reverse proxy (see .env.example)

volumes:
# All scenarios, programmes, users and sessions live here. A named volume
# survives image updates and rebuilds; back it up regularly.
- simhub-data:/app/data

healthcheck:
# Alpine/slim runtime images ship no curl or wget, so probe with Node's
# built-in fetch (Node 18+). Exec (list) form is used deliberately: it
# avoids a shell AND keeps YAML from misreading the "://" / colon-space
# in the script as a nested mapping. A bare `test: node -e "a: b"` string
# containing a colon-space WOULD need quoting; the list form sidesteps it.
test: ["CMD", "node", "-e", "fetch('http://127.0.0.1:3000/api/health').then(r => process.exit(r.ok ? 0 : 1)).catch(() => process.exit(1))"]
interval: 30s
timeout: 5s
start_period: 10s
retries: 3

volumes:
simhub-data:
30 changes: 0 additions & 30 deletions docker-compose.yml

This file was deleted.

Loading