This is the FOG 2.0 parent repository — a Node.js (Sails.js) rewrite of the FOG Project server, backed by MongoDB.
Work in progress. See docs/roadmap.md for the current state and the plan toward feature parity with FOG 1.6.
- Node.js — tested on Node 22 (LTS) and Node 26.
- MongoDB — 7.x recommended (primary datastore).
- Redis — 7.x (session store). Required: CSRF protection stores its secret in the session, so login and every form submit fail without it.
A fresh clone comes up in a few steps. The bundled compose file starts both MongoDB and Redis:
# 1. Install dependencies
npm install
# 2. Start MongoDB + Redis (no auth — dev only)
docker compose up -d # or: podman compose up -d
# No compose plugin? Run them directly:
# podman run -d --name fog-node-mongo -p 127.0.0.1:27017:27017 docker.io/library/mongo:7
# podman run -d --name fog-node-redis -p 127.0.0.1:6379:6379 docker.io/library/redis:7-alpine
# 3. Write dev config (config/local.js + config/models.js) + seed the Administrator
npm run setup:dev
# 4. (Optional) load sample data to click around -- a storage group + node,
# a couple of images, and a handful of hosts. Resets the dev database.
npm run seed:dev
# 5. Start the server
npm start # or: node app.jsThen browse to http://localhost:1337 and log in as Administrator
(default password fogadmin1 — change it, or override at setup time).
setup:dev writes the git-ignored config/local.js and config/models.js
with localhost defaults (Mongo on :27017, Redis via config/session.js on
:6379, migrate: 'safe'). Override any of them via env vars (see the header of
tools/setup/dev.js), e.g.:
FOG_DEV_ADMIN_PASSWORD='supersecret' FOG_DEV_DB_NAME='fogdev' npm run setup:devHeads-up: the schema uses
migrate: 'safe'(never auto-migrate — Mongo is schemaless, so none is needed). Don't point two app instances at one database withmigrate: 'alter'; that can wipe it.
For a real install, run the interactive installer:
npm run setup # prompts for DB, admin account, webserver, etc.
npm start # NODE_ENV=production node app.jsYou still need MongoDB and Redis reachable. The installer writes the DB
connection; sessions default to redis://127.0.0.1:6379/0 in
config/session.js — point that at your Redis. For a durable
systemd + Podman (Quadlet) deployment behind nginx — covering the Mongo and Redis
containers, the service units, and the reverse proxy — see the Persistent
deployment section below.
To run fog-node as a managed service that survives reboots — and reach it on the
standard ports (80/443) behind nginx instead of :1337 — wire it up with systemd
and a reverse proxy. The example below targets a Linux host with rootless
Podman; adjust the IP, user, and paths to your machine.
~/.config/containers/systemd/fog-node-mongo.container:
[Unit]
Description=fog-node MongoDB (podman)
After=network-online.target
Wants=network-online.target
[Container]
ContainerName=fog-node-mongo
Image=docker.io/library/mongo:7
PublishPort=127.0.0.1:27017:27017
Volume=fog-node-mongo-data:/data/db
[Service]
Restart=always
TimeoutStartSec=300
[Install]
WantedBy=default.targetsystemctl --user daemon-reload generates fog-node-mongo.service from this
file. The named volume fog-node-mongo-data keeps your data across container
re-creation.
Redis backs the session store, which is required for CSRF to work (the CSRF
token's secret lives in the session; the in-memory default doesn't persist it
reliably and wipes every login on restart).
~/.config/containers/systemd/fog-node-redis.container:
[Unit]
Description=fog-node Redis (session store)
After=network-online.target
Wants=network-online.target
[Container]
ContainerName=fog-node-redis
Image=docker.io/library/redis:7-alpine
PublishPort=127.0.0.1:6379:6379
Volume=fog-node-redis-data:/data
Exec=redis-server --appendonly yes
[Service]
Restart=always
TimeoutStartSec=300
[Install]
WantedBy=default.targetThen point Sails sessions at it in config/session.js:
module.exports.session = {
secret: '…',
adapter: '@sailshq/connect-redis',
url: 'redis://127.0.0.1:6379/0',
};~/.config/systemd/user/fog-node.service (replace <user>):
[Unit]
Description=fog-node (Sails.js) server on :1337
After=fog-node-mongo.service fog-node-redis.service network-online.target
Wants=fog-node-mongo.service fog-node-redis.service network-online.target
StartLimitIntervalSec=0
[Service]
Type=simple
WorkingDirectory=/home/<user>/fog-node
Environment=PATH=/usr/local/bin:/usr/bin:/bin
ExecStart=/usr/local/bin/node app.js
Restart=on-failure
RestartSec=5
[Install]
WantedBy=default.targetRestart=on-failure also covers the boot race where fog-node lifts before
MongoDB/Redis are accepting connections — it just retries.
# let your user's services start at boot without an active login session
sudo loginctl enable-linger "$USER"
systemctl --user daemon-reload
systemctl --user enable --now fog-node-mongo.service
systemctl --user enable --now fog-node-redis.service
systemctl --user enable --now fog-node.serviceManage with systemctl --user {status,restart,stop} fog-node; follow logs via
journalctl --user -u fog-node -f.
Sails uses socket.io, so the proxy must forward WebSocket upgrades or the live
UI/sockets break. /etc/nginx/conf.d/fog-node.conf (replace 10.0.0.10 with your
host's IP):
map $http_upgrade $fognode_connection_upgrade {
default upgrade;
'' close;
}
server {
listen 80;
server_name 10.0.0.10;
client_max_body_size 3000m;
location / {
proxy_pass http://127.0.0.1:1337;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $fognode_connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 86400;
}
}
server {
listen 10.0.0.10:443 ssl;
server_name 10.0.0.10;
client_max_body_size 3000m;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_certificate /etc/nginx/certs/fog-node.crt;
ssl_certificate_key /etc/nginx/certs/fog-node.key;
location / {
proxy_pass http://127.0.0.1:1337;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $fognode_connection_upgrade;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 86400;
}
}Self-signed cert for a LAN install, then test + reload:
sudo mkdir -p /etc/nginx/certs
sudo openssl req -x509 -nodes -newkey rsa:2048 -days 3650 \
-keyout /etc/nginx/certs/fog-node.key -out /etc/nginx/certs/fog-node.crt \
-subj "/CN=10.0.0.10" -addext "subjectAltName=IP:10.0.0.10"
sudo nginx -t && sudo systemctl reload nginxGotchas
- If you host other nginx vhosts, give each its own
ssl_session_cachezone name (or omit it). Duplicateshared:NAME:sizezones with mismatched sizes makenginx -tfail.- The app's entry point is
/login; a bare/returns 403 by design.
TODO: Build views for all the different components Build systemctl script (linux) — documented above ("Persistent deployment") Build launchctl script (macos) Build Service/Task Scheduler to start script (windows) Test Testing again