-
-
Notifications
You must be signed in to change notification settings - Fork 0
What the Setup Script Does
This is a line-by-line account of everything setup.sh and the wizard
they launch (scripts/setup-wizard.js) install, configure, write to disk, or send over the network —
for anyone who wants to know exactly what they're running before they run it, or is troubleshooting a
step that failed partway through. For the usage walkthrough (what the form asks and what to pick),
see the Installation Guide.
Nothing here runs with elevated privileges until explicitly noted, and every sudo
prompt is called out below — the script never silently escalates.
Testing scope. The Linux path (
setup.sh) has only actually been run on Rocky Linux 9 and Ubuntu 24.04/26 — Ubuntu tested as a VM and on bare metal, not as an LXC container (see Ubuntu LXC is not recommended in the Installation Guide) — other distros follow the same logic but haven't been as thoroughly exercised. This script covers a lot of environment variation (package managers, service managers, Docker installers) automatically, and won't be perfect on every system. If something breaks, please report it — GitHub issues — with your OS/version and what you ran, so it can get fixed for the next person. In the meantime, Manual / non-Docker deployment or the manualdocker composecommands are always a fallback if the script itself doesn't work on your system.All of the above is x86_64 only — ARM (e.g. Raspberry Pi) is untested, but should mostly work: Node and Docker Engine both officially support Debian/Ubuntu on arm64/armhf. Pick "Build locally" — the published image (
ghcr.io/j5guy/fondwaypoints) is only built for amd64 (see CI/CD and Releases), so "pull a pre-built image" won't work there. Building locally compilesbcryptfrom source as a fallback if no prebuilt binary exists for your platform (see the Dockerfile step below) — slower, but should still succeed.
Running ./setup.sh does three things in order:
-
Install Node.js, if it isn't already on
PATH. -
Run
npm install, ifnode_modules/doesn't exist yet — installs this project's dependencies (Express, Mongoose, bcrypt, etc.) from the public npm registry intonode_modules/inside the repo. Nothing outside the repo is touched by this step. -
Launch the setup wizard (
node scripts/setup-wizard.js) — a small local web server that collects your.envvalues, then brings the app up. This is where the interesting stuff happens; see below.
If .env already exists, you're asked whether to re-run the wizard (to change settings) or skip it —
skipping just redeploys with whatever's already in .env and .deploy-state.json (see
Re-running later).
Only runs if node isn't found on PATH. Exact commands, per platform:
-
Linux (
setup.sh): NodeSource's install script, via whichever package manager is present —curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -thensudo apt-get install -y nodejs(Debian/Ubuntu), or thednfequivalent (Fedora/RHEL/Rocky). If neitherapt-getnordnfis found, the script stops and asks you to install Node manually. Ifcurlitself isn't present (some minimal images, recent Ubuntu included, don't ship it) it's installed first via the same package manager — the Docker install path (below) does the same check before its owncurluse.
Only runs if node_modules/ doesn't already exist (so a re-run doesn't redo it every time). Plain
npm install from the project root, pulling this project's dependencies (Express, Mongoose, bcrypt,
etc. — see package.json) from the public npm registry into node_modules/. Nothing outside the repo
is touched — no system-wide packages, no config written anywhere else.
scripts/setup-wizard.js starts a plain Express server, bound to your machine (not the public
internet) on the first free port from 5599 up, and opens it in your default browser automatically. If
you're on a headless server with no GUI, it also prints a LAN URL (http://<your-lan-ip>:<port>/) you
can open from another computer — anyone else on that network can reach this page, unauthenticated,
until you submit the form, so keep that window short on a shared network.
While the form is open, the wizard also does two small live-check requests as you type, both entirely local:
-
"generate" buttons next to
sessionSecret/PRELOGIN_SECRETcall back to the wizard itself, which returnscrypto.randomBytes(64).toString('hex')— generated locally, never sent anywhere. -
SSL_CERT_FILE/SSL_KEY_FILElive check — as you type a path, the wizard callsfs.statSync()on its own host (the machine running the wizard, which is also the machine Docker will read the bind-mounted cert from) to show a ✓/✗ next to the field.
Submitting the form (POST /save) does the following, in order:
-
(Docker + "Generate one" cert mode only) Generates a local TLS certificate. Entirely local via
the
opensslCLI — nothing is sent over the network. Creates (or reuses, if already present) a Certificate Authority atcerts/ca.key/certs/ca.pem, then issues a leaf certificate atcerts/cert.key/certs/cert.pemforWEB_FQDN, with SAN entries coveringWEB_FQDNand this machine's LAN IP address(es) plus127.0.0.1. The CA's private key never leaves this step — it stays incerts/. This certificate isn't trusted by anything yet — see Import the certificate first — you (and anyone else who needs to reach the site) still need to install the downloaded CA certificate into each device's OS/browser trust store before visiting the site there. -
Writes
.env(file mode0600, project root) with everything you filled in. -
Writes
.deploy-state.json— non-secret bookkeeping (deploy mode, service name/user, Mongo mode, cert mode, image source) so a later re-run ofsetup.shwithout reopening the wizard still knows how to redeploy. - Brings the app up, one of two ways depending on what you picked:
-
Installs Docker, only if it's missing, and only now that you've actually chosen Docker — never
preemptively. Exact commands:
-
Linux, Rocky specifically:
sudo dnf install dnf-plugins-core, then registers Docker's RHEL repo directly (download.docker.com/linux/rhel/docker-ce.repo— Docker's own Rocky repo is missing packages, so this sidesteps that) and installsdocker-ce docker-ce-cli containerd.io docker-compose-plugin docker-ce-rootless-extras docker-buildx-pluginviasudo dnf. -
Linux, everything else: Docker's official convenience script —
curl -fsSL https://get.docker.com | sudo sh. -
Linux only, after either path:
sudo systemctl enable --now docker(starts Docker and sets it to start on boot) andsudo usermod -aG docker <your username>(lets you rundockerwithoutsudo— needs a fresh login/newgrp dockerto take effect).
-
Linux, Rocky specifically:
-
Runs
docker compose up -d(with--buildunless you picked "pull a pre-built image", in which case it'sdocker compose pullfirst) using whichever compose files apply:docker-compose.ymlalways, plusdocker-compose.mongo.ymlif you picked internal MongoDB, plusdocker-compose.pull.ymlif pulling. This is what actually builds/pulls and starts the containers:app(this application),nginx(TLS termination — reads the cert from step 1 or your own files), andmongo(only if internal MongoDB was picked). - Inside the
appimage build itself (seeDockerfile),npm install --omit=devand the SCSS build (sass→public/css/main.css) both happen — you don't need Node installed on the Docker host for this path beyond what the wizard itself needed to run. Thatnpm installtemporarily installsbuild-essential/python3first and purges them again afterward, in the same image layer — a fallback sobcrypt's native module compiles from source on platforms without a prebuilt binary for it (e.g. possibly ARM — see the testing-scope note above) instead of failing the build outright.
No Docker involved at all. Runs npm install and npm run build-css (compiles
public/scss/main.scss → public/css/main.css), then registers node server.js as a background
service so it survives reboots and restarts on crash:
Writes a systemd unit to /etc/systemd/system/<service-name>.service (via sudo tee —
you'll be prompted for a sudo password), then sudo systemctl daemon-reload, enable, and
restart. Runs as whatever user you specified in the form (defaults to whoever ran the wizard).
This mode does not set up MongoDB, TLS, or a reverse proxy for you — point mongoHost in .env at
an existing MongoDB server, and put your own TLS-terminating reverse proxy in front of the app
yourself. See Local Node.js process, without the wizard
for the manual equivalent of everything above.
Once Docker (or the local service) is actually up, the last thing printed is the URL(s) the app is
reachable at — WEB_FQDN plus this machine's LAN IP address(es), scheme and port included (e.g.
https://fondwaypoints.example.com:5560/). 127.0.0.1 is deliberately never listed — nothing outside
this machine can reach the site there, so printing it would be misleading. The same happens on a later
run of setup.sh that skips reopening the wizard and just redeploys from
the existing .env (via scripts/printAccessUrls.js, reading WEB_FQDN/PORT from it directly).
| Path | What | Committed to git? |
|---|---|---|
node_modules/ |
npm dependencies | No (.gitignore) |
.env |
Your secrets/config, mode 0600
|
No (.gitignore) |
.deploy-state.json |
Non-secret deploy bookkeeping, so re-runs know what to do | No (.gitignore) |
certs/ca.key, certs/ca.pem
|
Local CA, only if you picked "Generate one" | No (.gitignore) |
certs/cert.key, certs/cert.pem, certs/cert.csr, certs/cert.ext
|
Leaf cert issued by the above | No (.gitignore) |
public/css/main.css |
Compiled from public/scss/
|
No (.gitignore) |
logs/ |
App logs (Winston); also service stdout/stderr | No (.gitignore) |
/etc/systemd/system/<name>.service (local mode) |
systemd unit | Outside the repo entirely |
-
Package managers/installers, only for whatever's actually missing: NodeSource/
apt/dnf(Node on Linux), Docker'sget.docker.comscript or RHEL repo (Docker on Linux). -
The npm registry, for
npm install(this project's dependencies) — both directly (local/Docker build) and inside the CI-published image build. -
Debian's package mirrors, inside the Docker image build only, to install (and then remove again)
build-essential/python3— thebcryptnative-module fallback mentioned above. -
Docker Hub, to pull the
node:20-bookworm-slimbase image the first time you build locally (or whenever it's changed) — cached after that. Other Docker registries, only if you picked "Pull a pre-built image" instead — pulls fromFONDWAYPOINTS_IMAGE(defaults toghcr.io/j5guy/fondwaypoints:latest). - Whatever you configure: the MongoDB host and SMTP relay you enter into the form — the app talks to those once it's running, same as any deployment.
- Nothing else. The wizard's own web server is local/LAN-only and never phones home; there's no telemetry or analytics anywhere in this project.
sudo is used only for:
- Installing Node.js or Docker system packages (Steps 1 and 4).
-
sudo systemctl enable --now dockerandsudo usermod -aG docker <user>(Linux Docker install). - Writing the systemd unit and enabling/starting the service (local deploy mode).
If sudo isn't available at all (and you're not already root), the relevant step fails with a message
telling you so rather than silently skipping it.
Re-running setup.sh after .env already exists asks whether to reopen the
wizard (to change settings — prefilled with your current values) or leave .env alone and just
redeploy/restart with what's already there, using .deploy-state.json to know which mode you're in.
Local-mode service installs use restart (not a fresh enable), and the Docker CA is
reused rather than regenerated, so a re-run won't invalidate CA trust you've already installed on other
devices.