Production Installation
If you’ve become convinced you would like a “production” BBS running ENiGMA½ a more advanced installation may be in order.
PM2 is an excellent choice for managing your running ENiGMA½ instances if you’ve installed via the install script or manual installation method. Additionally, it is suggested that you run as a specific more locked down user (e.g. ‘enigma’).
If you’re running ENiGMA via Docker, then process management is already handled for you!
Running Under systemd
Section titled “Running Under systemd”On Linux distributions using systemd, a unit file is a clean way to keep ENiGMA½ running and have it
restart on failure. Below is a minimal example assuming a dedicated enigma user with the BBS installed
to /home/enigma/xibalba:
[Unit]Description=ENiGMA½ BBSAfter=network.target
[Service]ExecStart=/home/enigma/xibalba/misc/start.shWorkingDirectory=/home/enigma/xibalba/User=enigmaGroup=enigmaRestart=on-failureKillMode=control-groupEnvironment=NODE_ENV=production# See "Node.js managed by mise / nvm / asdf" below for why this is neededEnvironment=PATH=/home/enigma/.local/share/mise/shims:/usr/local/bin:/usr/bin:/binStandardOutput=journalStandardError=journal
[Install]WantedBy=multi-user.targetA matching start.sh is just:
#!/usr/bin/env bashexec node main.jsThen enable and start it:
sudo systemctl daemon-reloadsudo systemctl enable --now xibalbajournalctl -u xibalba -fNode.js managed by mise / nvm / asdf
Section titled “Node.js managed by mise / nvm / asdf”The install script installs Node.js via mise by default.
Version managers like mise, nvm, and asdf normally hook into your interactive shell (e.g. via
mise activate in ~/.bashrc) — but systemd services do not run an interactive shell, so that
hook never fires. Without help, node will not be on the service’s PATH, and any child process
that relies on a #!/usr/bin/env node shebang (e.g. oputil.js, util/exiftool2desc.js) or any
scheduled event using @execute:node in config.hjson will fail with an ENOENT-style error.
The fix is to put the version manager’s shims directory on the unit’s PATH. Shims are plain
executables that dispatch to the currently-active managed version, so they work fine in
non-interactive contexts. For mise (default install location):
Environment=PATH=/home/enigma/.local/share/mise/shims:/usr/local/bin:/usr/bin:/binFor nvm or asdf, substitute the appropriate shims/bin directory. Once this is set, you can use
plain node everywhere — including in start.sh and in scheduled event @execute: actions —
and mise upgrade node (or the equivalent) will not require any path changes.
Verify after daemon-reload and restart:
systemctl show xibalba -p EnvironmentSELinux (RHEL, Fedora, Rocky, Alma, …)
Section titled “SELinux (RHEL, Fedora, Rocky, Alma, …)”On SELinux-enforcing distributions, systemd will refuse to execute a script that lives under a
user’s home directory unless the file has an appropriate type label (e.g. bin_t). Symptoms look
like this in the journal:
xibalba.service: Failed at step EXEC spawning /home/enigma/xibalba/misc/start.sh: Permission deniedxibalba.service: Main process exited, code=exited, status=203/EXECPersistently relabel start.sh (and any other executables systemd needs to run directly) so the
context survives editor saves and restorecon runs:
sudo semanage fcontext -a -t bin_t '/home/enigma/xibalba/misc/start\.sh'sudo restorecon -v /home/enigma/xibalba/misc/start.shStopping
Section titled “Stopping”ENiGMA½ shuts down in order on SIGINT (Ctrl+C, and what PM2 sends) and on SIGTERM (what
systemd, Docker and kill send): it stops accepting new connections, tells every connected caller
the server is going down and disconnects them, gives running Amiga doors up
to about 3 seconds to sync their disk images, stops the event scheduler and the message networks,
and exits with status 0. That normally takes well under 5 seconds. If a step hangs, ENiGMA½ gives
up on it after 15 seconds, logs which step it was, and exits with status 1. A second signal while
it is shutting down is ignored. A signal in the first moments after launch (about 100 ms, before
the log’s ENiGMA½ Bulletin Board System Starting Up! line) still ends the process at once,
which is harmless because nothing is running yet.
Whatever runs it has to allow it that long before killing it:
-
systemd:
TimeoutStopSecdefaults to 90 seconds, which is plenty. Don’t lower it below 20 seconds. -
PM2:
kill_timeoutdefaults to 1600 ms, after which PM2 sendsSIGKILL, often in the middle of the shutdown. Set it to 20000 in your ecosystem file:ecosystem.config.js module.exports = {apps: [{ name: 'enigma-bbs', script: 'main.js', kill_timeout: 20000 }],};or start it with
pm2 start main.js --kill-timeout 20000. -
Docker: the image’s entrypoint sets
PM2_KILL_TIMEOUT=20000forpm2-runtime; pass-e PM2_KILL_TIMEOUT=...to change it. Docker itself kills the container 10 seconds afterdocker stop, so give it longer:docker stop -t 30 ENiGMABBS, orstop_grace_period: 30sin Compose.