Overview
Platform ownershipAdmins own the full runtime and commercial configuration of the platform. That includes install decisions, billing credentials, plan design, tenant placement, infrastructure pools, runtime health, and incident response.
- Core setup controls branding, billing credentials, landing-page content, AI provider keys, and docs visibility.
- SaaSForge controls tenants, domains, provisioning jobs, provisioning servers, health checks, backups, runtime dependencies.
- N8NForge controls managed instances, launch metadata, and workflow-template delivery.
Installation
First deploy- PHP 8.3+ with the Laravel and Wave requirements expected by the packaged app.
- Application database on MySQL, MariaDB, PostgreSQL, or SQLite.
- Queue and scheduler support because provisioning, templates, cleanup, and reminders depend on scheduled work.
- Docker-capable runtime hosts for the local Linux-host driver or any SSH provisioning target.
Extract the main package
Deploy the versioned install ZIP into the target app directory and point the web root
at public/.
Prepare writable paths
Ensure Laravel can write to storage/ and
bootstrap/cache/, then provide a valid .env.
Run the installer
Open /install, finish the environment and database steps, then create
the
first admin account.
Log in and complete product setup
Move immediately into Setup, Plans, Themes, and provisioning resources before testing real customer flows.
First-day checklist
Recommended order- Configure Setup for General, Billing, Landing Page, AI, and Authentication.
- Review plans and assign real billing price IDs plus any plan-level delivery mappings.
- Choose the public theme and replace seeded marketing content.
- Enable scheduler execution so provisioning and health automation can run.
- Create provisioning resources such as SSH servers, external databases, and external storage backends if you will not rely only on the default local runtime path.
- Run a real test subscription and confirm it creates a tenant, queues a job, and produces a usable runtime instance page. or custom domains.
Setup page
Shared admin configThe Core Setup page is the main live configuration surface for branding, billing credentials, marketing content, AI provider keys, and authentication presentation.
Site title, description, logos, icons, analytics, and docs visibility in app menus.
Stripe or Paddle provider selection plus encrypted credential and webhook storage.
Hero, CTA, stats, pricing copy, testimonials, and public marketing sections.
Encrypted provider keys for OpenAI, Anthropic, Grok, and DeepSeek.
Shared auth-related setup exposed through the same admin flow.
Database-backed setup values are loaded into runtime config and become the live source of truth.
Key env reference
Operator-level settings| Variable | Default | Purpose |
|---|---|---|
| SaaSForge — Base | ||
SAASFORGE_WORKSPACE_SUFFIX |
Workspace |
Suffix appended to tenant workspace names |
SAASFORGE_PROVISIONING_DRIVER |
docker |
Default runtime driver (docker / ssh) |
SAASFORGE_PROVISIONING_QUEUE |
default |
Queue connection for provisioning jobs |
SAASFORGE_PROVISIONING_BATCH_SIZE |
10 |
Max jobs processed per scheduler run |
SAASFORGE_SHOW_CADDY_FIX_BUTTONS |
false |
Show admin UI buttons for Caddy repair |
SAASFORGE_BASE_DOMAIN |
app.url host | Base domain for generated tenant subdomains |
SAASFORGE_AUTO_SSL |
true |
Auto-provision SSL via Let's Encrypt |
SAASFORGE_PRODUCT_TYPE |
n8nforge |
Default product type for new tenants |
SAASFORGE_AUTO_CREATE_FIRST_INSTANCE |
true |
Auto-create the first n8n instance when a tenant is created from a subscription |
| SaaSForge — Docker Runtime | ||
SAASFORGE_LINUX_RUNTIME_ENABLED |
true |
Enable the local Docker driver |
SAASFORGE_RUNTIME_ROOT_PATH |
storage/.../runtime |
Host path for compose files and runtime data |
SAASFORGE_DOCKER_BIN |
docker |
Docker binary path |
SAASFORGE_DOCKER_COMPOSE_COMMAND |
docker compose |
Compose command |
SAASFORGE_RUNTIME_NETWORK |
saasforge |
Docker network name for runtime containers |
SAASFORGE_RUNTIME_IMAGE |
docker.n8n.io/n8nio/n8n:latest |
n8n container image tag |
SAASFORGE_RUNTIME_BASE_PORT |
23000 |
Starting port for container port mapping |
SAASFORGE_RUNTIME_PORT_STEP |
1 |
Port increment between instances |
SAASFORGE_RUNTIME_WAIT_TIMEOUT |
180 |
Seconds to wait for container health |
SAASFORGE_RUNTIME_TIMEZONE |
UTC |
Timezone set on runtime containers |
SAASFORGE_RUNTIME_CPUS |
0.5 |
Default CPU limit per container |
SAASFORGE_RUNTIME_MEMORY |
600m |
Default memory limit per container |
SAASFORGE_RUNTIME_MEMORY_SWAP |
700m |
Default swap limit per container |
SAASFORGE_RUNTIME_RUNNERS_ENABLED |
true |
Enable n8n task runners |
| SaaSForge — Caddy (Docker) | ||
SAASFORGE_RUNTIME_CADDY_ENABLED |
true |
Manage Caddy reverse-proxy sites |
SAASFORGE_RUNTIME_CADDY_BIN |
caddy |
Caddy binary path |
SAASFORGE_RUNTIME_CADDY_RELOAD |
true |
Run caddy reload after site changes |
SAASFORGE_RUNTIME_CADDY_MAIN_CONFIG |
/etc/caddy/Caddyfile |
Main Caddyfile path |
SAASFORGE_RUNTIME_CADDY_SITE_DIR |
storage/.../caddy-sites |
Directory for per-instance site files |
SAASFORGE_RUNTIME_CADDY_TLS_EMAIL |
MAIL_FROM_ADDRESS | Email for Let's Encrypt certificate notices |
| SaaSForge — SSO / OIDC | ||
SAASFORGE_RUNTIME_N8N_SSO_ENABLED |
false |
Enable OIDC SSO for instance login |
SAASFORGE_RUNTIME_N8N_SSO_PROTOCOL |
oidc |
SSO protocol (oidc) |
SAASFORGE_RUNTIME_N8N_SSO_MANAGED_BY_ENV |
true |
Inject SSO config via container env vars |
SAASFORGE_RUNTIME_N8N_SSO_USER_ROLE_PROVISIONING |
disabled |
Auto-provision n8n user roles on login |
SAASFORGE_RUNTIME_N8N_SSO_OIDC_LOGIN_ENABLED |
true |
Show OIDC login button on n8n sign-in |
SAASFORGE_RUNTIME_N8N_SSO_OIDC_CLIENT_ID |
n8n-sso |
OIDC client ID |
SAASFORGE_RUNTIME_N8N_SSO_OIDC_CLIENT_SECRET |
generated | OIDC client secret (regenerated on install) |
SAASFORGE_RUNTIME_N8N_SSO_OIDC_DISCOVERY_ENDPOINT |
app.url /.well-known/… | OIDC discovery URL |
SAASFORGE_RUNTIME_N8N_SSO_OIDC_PROMPT |
empty | OIDC prompt parameter (login, consent, etc.) |
SAASFORGE_RUNTIME_N8N_SSO_OIDC_ACR_VALUES |
empty | OIDC acr_values parameter |
| SaaSForge — SSH Runtime | ||
SAASFORGE_SSH_RUNTIME_ENABLED |
true |
Enable the SSH runtime driver |
SAASFORGE_SSH_CONNECT_TIMEOUT |
10 |
SSH connection timeout in seconds |
SAASFORGE_SSH_WAIT_TIMEOUT |
180 |
Wait timeout for remote operations |
SAASFORGE_SSH_RUNTIME_ROOT_PATH |
/var/lib/saasforge/runtime |
Remote path for runtime files |
SAASFORGE_SSH_DOCKER_BIN |
docker |
Remote Docker binary path |
SAASFORGE_SSH_DOCKER_COMPOSE_COMMAND |
docker compose |
Remote compose command |
SAASFORGE_SSH_RUNTIME_IMAGE |
docker.n8n.io/n8nio/n8n:latest |
Remote n8n image tag |
SAASFORGE_SSH_RUNTIME_BASE_PORT |
23000 |
Remote base port |
SAASFORGE_SSH_RUNTIME_CPUS |
0.5 |
Remote CPU limit |
SAASFORGE_SSH_RUNTIME_MEMORY |
600m |
Remote memory limit |
SAASFORGE_SSH_RUNTIME_MEMORY_SWAP |
700m |
Remote swap limit |
SAASFORGE_SSH_RUNTIME_RUNNERS_ENABLED |
true |
Remote task runners |
SAASFORGE_SSH_CADDY_RELOAD |
true |
Remote Caddy reload after site changes |
SAASFORGE_SSH_CADDY_BIN |
caddy |
Remote Caddy binary path |
SAASFORGE_SSH_CADDY_MAIN_CONFIG |
/etc/caddy/Caddyfile |
Remote main Caddyfile |
SAASFORGE_SSH_CADDY_SITE_DIR |
/etc/caddy/sites-enabled/saasforge |
Remote Caddy site files directory |
| SaaSForge — Backups & Health | ||
SAASFORGE_BACKUPS_ENABLED |
true |
Enable scheduled runtime backups |
SAASFORGE_BACKUPS_MAX_PER_TENANT |
7 |
Max backups retained per tenant |
SAASFORGE_HEALTH_CHECKS_ENABLED |
true |
Enable scheduled health checks |
SAASFORGE_HEALTH_CURL_TIMEOUT |
5 |
Health check HTTP timeout in seconds |
SAASFORGE_HEALTH_FAILURE_THRESHOLD |
3 |
Consecutive failures before instance marked unhealthy |
| SaaSForge — Domains & TLS | ||
SAASFORGE_CADDY_ON_DEMAND_TLS_ENABLED |
true |
Enable Caddy on-demand TLS for custom domains |
| N8NForge — Instances | ||
N8NFORGE_PRODUCT_TYPE |
n8nforge |
Product type for N8NForge instances |
N8NFORGE_DEFAULT_N8N_VERSION |
latest |
Default n8n version tag for new instances |
N8NFORGE_INSTANCE_SUBFOLDER |
/ |
Subfolder path for instance URL |
N8NFORGE_INSTANCE_RANDOM_SLUG_PREFIX |
inst |
Prefix for random slug when instance slug would repeat the tenant slug |
| N8NForge — Templates | ||
N8NFORGE_AUTO_INSTALL_FEATURED |
true |
Auto-queue featured templates on new workspaces |
N8NFORGE_AUTO_INSTALL_STATUS |
queued |
Status for auto-installed templates |
N8NFORGE_MANUAL_INSTALL_STATUS |
queued |
Status for manually installed templates |
| N8NForge — Metrics | ||
N8NFORGE_METRICS_POLLING |
false |
Enable background metrics polling via scheduler |
N8NFORGE_METRICS_POLLING_INTERVAL |
15 |
Minutes between metric collection cycles |
N8NFORGE_METRICS_RETENTION_DAYS |
60 |
Days to retain metric snapshots before cleanup |
| N8NForge — Updates | ||
N8NFORGE_UPDATE_SERVER_URL |
empty | Update server URL for platform releases |
N8NFORGE_UPDATE_REQUEST_TIMEOUT |
120 |
Timeout for update server requests |
Branding, billing keys, landing copy, and docs visibility are meant to be changed from admin. Runtime topology, Docker and proxy paths, domain defaults, and SSO settings remain infrastructure configuration.
Instance resource limits
Every n8n container gets default CPU, memory, and swap limits. These can be overridden per instance from the admin panel.
- Open Automation → Managed Instances → Edit on the instance you want to adjust.
- The Resource Overrides section lets you set CPU, memory, memory swap, and toggle task runners.
- When saved, the instance restarts automatically to apply the new limits.
Metrics collection
Each n8n instance exposes a metrics endpoint that feeds the customer dashboard stat cards, history bars, and the admin Top Resource Consumers widget (shows the 10 instances with highest memory usage).
Metrics are captured automatically when customers view their dashboard. An optional
background poller can be enabled via N8NFORGE_METRICS_POLLING=true to collect
data across all instances regardless of dashboard activity. Snapshots are stored in the
n8nforge_instance_metrics table.
Retention & cleanup
Old snapshots can be pruned automatically to keep the metrics table lean:
- Retention window: controlled by
N8NFORGE_METRICS_RETENTION_DAYS(default 60). Rows older than this threshold are candidates for deletion. - Manual cleanup:
php artisan n8nforge:clean-metrics— add--dry-runto preview without deleting. - Scheduled cleanup: the framework console kernel registers
n8nforge:clean-metricson thedailyschedule. Make sure the Laravel scheduler runs every minute via cron:* * * * * cd /path/to/project && php artisan schedule:run.
Plans and billing
Commercial delivery- Provider choose Stripe or Paddle in Setup.
- Credentials save publishable, secret, and webhook values in encrypted storage.
- Plan pricing enter monthly, yearly, or one-time provider price IDs on the plan resource.
- Role assignment map each plan to the correct post-subscription role.
- Free tier handling mark the fallback plan if unsubscribed users should keep some access.
- Delivery policy use the plan resource to map provisioning servers, external databases, and external storage pools to each offer.
SaaSForge stores the active Wave plan_id on the tenant as
product_reference. That value is what server and runtime dependency
selectors
use when applying plan-level placement rules.
Provisioning model
Runtime orchestrationProvisioning is driver-based and job-driven. Initial deploy, suspend, resume, restart, redeploy, and domain sync are all tracked lifecycle jobs.
- Driver selection persists as
provisioning_driveron the tenant. - Server placement persists as
provisioning_server_idso later health, backup, and lifecycle actions use the same host. - Dependency placement persists selected external database and storage backends so restarts and redeploys keep using the same infrastructure choices.
- Provisioning jobs are the audit trail for all tracked runtime actions.
Uses the application host as the runtime host.
Uses reusable remote provisioning servers selected from plan mappings.
The same placement model can support later API-based runtime providers.
Provisioning servers define SSH connection settings, runtime defaults, reverse proxy behavior, and optional plan-level tenant capacity. Treat them as the actual hosts that carry tenant traffic.
Domain sync does not register domains with a DNS provider and does not use a wildcard
Caddy listener with an ask endpoint back to the main app. Instead,
SaaSForge
writes a concrete Caddy site file for each tenant using that tenant's exact
domain_hosts list, then reloads Caddy and updates the n8n container
environment so N8N_HOST, N8N_EDITOR_BASE_URL, and
WEBHOOK_URL point at the tenant's primary domain.
- Docker and Compose must already be installed on the provisioning host and available to the configured deploy user.
- Caddy must already be installed if you enable reverse-proxy management for that server. SaaSForge writes per-tenant site files and reloads Caddy, but it does not install or bootstrap Caddy for you.
- A writable runtime root must exist for compose files, env files, logs, backups, and tenant data.
- A shared Docker network must be available or creatable by the deploy user for tenant containers.
- DNS must point tenant domains at the provisioning host or fronting load balancer before HTTPS traffic can succeed.
- Firewall and ports must allow public HTTP/HTTPS traffic to the reverse proxy and local container traffic on the allocated host ports.
- Prepare the host with Docker, Docker Compose, Caddy, and a deploy user that can write the runtime directories and reload Caddy.
- Point DNS for any tenant or base-domain records at that host or its fronting proxy/load balancer.
- Create a provisioning server in admin with the SSH connection, root path, Docker command names, network, and optional Caddy paths.
- Assign the server to plans so new tenants can be placed onto that host automatically.
- Run a real provisioning test and confirm the tenant compose files, Caddy site file, Caddy reload, HTTPS response, and n8n launch URL all succeed.
- Creates tenant runtime files such as the compose file, env file, log path, backup path, and tenant data path under the configured runtime root.
- Creates or updates a tenant-specific Caddy site file that contains the tenant's exact domain list and reverse proxies traffic to that tenant's allocated host port.
- Starts, stops, restarts, or redeploys the tenant container with the configured Docker and Compose commands.
- Reloads Caddy after writing or removing a tenant site file when reverse proxy management is enabled.
- Updates n8n runtime URLs so the primary tenant domain becomes the editor base URL and webhook base URL.
- It does not install Docker or Docker Compose on the provisioning host.
- It does not install or bootstrap Caddy on the provisioning host.
- It does not create DNS records with Cloudflare, Route53, or any other DNS provider.
- It does not create a wildcard or catch-all Caddy listener that asks the central app whether a domain is allowed.
- It does not manage operating-system firewall rules or cloud load balancer listeners for you.
Treat provisioning hosts as pre-built runtime targets. SaaSForge expects the server to be ready for deployments before you save it in admin.
#!/usr/bin/env bash
set -euo pipefail
DEPLOY_USER="${1:-deploy}"
HOSTNAME="$(hostname -f)"
_has_cmd() { command -v "$1" || return 1; }
_deb_installed() { dpkg -s "$1" || return 1; }
_install_if_missing() {
local missing=()
for pkg in "$@"; do
_deb_installed "$pkg" || missing+=("$pkg")
done
[[ ${#missing[@]} -eq 0 ]] || sudo DEBIAN_FRONTEND=noninteractive apt-get install -y -qq "${missing[@]}"
}
echo "=== 1. System updates ==="
sudo apt-get update -qq
sudo apt-get upgrade -y -qq
_install_if_missing curl ufw unattended-upgrades
if ! swapon --show | grep -q .; then
sudo fallocate -l 1G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
fi
echo "=== 2. Docker ==="
if ! _has_cmd docker; then
for pkg in docker.io docker-doc docker-compose podman-docker containerd runc; do
sudo apt-get remove -y "$pkg" || true
done
curl -fsSL https://get.docker.com | sudo bash
fi
_install_if_missing docker-compose-plugin
echo "=== 3. Docker daemon config ==="
sudo mkdir -p /etc/docker
printf '%s\n' '{"log-driver":"json-file","log-opts":{"max-size":"10m","max-file":"3"},"live-restore":true,"storage-driver":"overlay2"}' \
| sudo tee /etc/docker/daemon.json
if _has_cmd dockerd; then
sudo systemctl restart docker
fi
echo "=== 4. Caddy ==="
if ! _has_cmd caddy; then
_install_if_missing debian-keyring debian-archive-keyring apt-transport-https
sudo mkdir -p /usr/share/keyrings
if [[ ! -f /usr/share/keyrings/caddy-stable-archive-keyring.gpg ]]; then
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
| sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
fi
if [[ ! -f /etc/apt/sources.list.d/caddy-stable.list ]]; then
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
| sudo tee /etc/apt/sources.list.d/caddy-stable.list
fi
sudo apt-get update -qq
_install_if_missing caddy
fi
echo "=== 5. Deploy user & runtime dirs ==="
sudo id -u "$DEPLOY_USER" || sudo useradd -m -s /bin/bash "$DEPLOY_USER"
sudo usermod -aG docker "$DEPLOY_USER"
sudo mkdir -p /var/lib/saasforge/runtime
sudo chown -R "$DEPLOY_USER":"$DEPLOY_USER" /var/lib/saasforge/runtime
sudo mkdir -p /etc/caddy/sites-enabled/saasforge
echo "=== 6. Kernel tuning ==="
printf 'fs.file-max = 100000\nvm.max_map_count = 262144\nnet.core.somaxconn = 1024\n' \
| sudo tee /etc/sysctl.d/99-saasforge.conf
sudo sysctl --system
echo "=== 7. Firewall ==="
if sudo ufw status | grep -q 'Status: active'; then
for port in ssh 80/tcp 443/tcp; do
sudo ufw status | grep -q "$port" || sudo ufw allow "$port"
done
else
_install_if_missing ufw
sudo ufw --force reset
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw --force enable
fi
echo "=== 8. Enable services ==="
sudo systemctl enable --now docker
# Write a minimal Caddyfile so caddy can start
if ! caddy validate --config /etc/caddy/Caddyfile; then
printf ':80 {\n respond "SaaSForge provisioning host"\n}\n\nimport /etc/caddy/sites-enabled/saasforge/*\n' \
| sudo tee /etc/caddy/Caddyfile
fi
cd /etc/caddy && caddy start
echo "Caddy started in /etc/caddy/"
# unattended-upgrades is a timer, not a service
sudo systemctl enable unattended-upgrades || true
sudo systemctl start unattended-upgrades || true
echo "=== 9. Verification ==="
echo -n "Docker: "; sudo -u "$DEPLOY_USER" docker version --format '{{.Server.Version}}'
echo -n "Compose: "; sudo -u "$DEPLOY_USER" docker compose version --short
echo -n "Caddy: "; caddy version
echo -n "UFW: "; sudo ufw status | head -1
echo ""
echo "Host $HOSTNAME ready. Add it as a Provisioning Server in admin.
Host : $HOSTNAME
Port : 22
Username : $DEPLOY_USER"
The main Caddy config should import the SaaSForge-managed site directory. SaaSForge
writes
individual tenant site files into that directory and then reloads Caddy. If you want
on-demand TLS protection for tenant and custom domains, add the global
on_demand_tls ask endpoint that points at the central app.
# Example /etc/caddy/Caddyfile excerpt
{
email admin@example.com
on_demand_tls {
ask https://app.example.com/saasforge/caddy/on-demand-tls
}
}
import /etc/caddy/sites-enabled/saasforge/*
When SAASFORGE_CADDY_ON_DEMAND_TLS_ASK_URL is configured, SaaSForge-generated
site
files switch to tls { on_demand } and expect the main Caddy config to expose
the
matching ask URL above.
Point tenant domains or delegated subdomains at the provisioning host or its fronting load balancer before expecting HTTPS to succeed. Domain sync only updates the runtime and Caddy side after the hostname is already routed to the host.
A brand-new SSH target can fail on its first deployment if the app host has never trusted
that
server before. When strict host key checking is enabled, trust the host in
known_hosts first or disable strict checking temporarily for the first
test.
External runtime dependencies
Database and storage poolsManaged PostgreSQL pools with host, port, database, credentials, schema, pool sizing, optional SSL material, and plan-level capacity mapping.
Managed S3-compatible binary-data storage backends with endpoint, protocol, bucket, region, credentials, and plan-level capacity mapping.
- Fallback behavior remains local SQLite plus local filesystem storage when no external resource is assigned.
- Tenant overrides can explicitly pin a tenant to a specific database or storage backend.
- Plan mappings let you keep infrastructure placement policy on the product offer instead of per-tenant manual edits.
External binary-data storage depends on n8n enterprise licensing. If you are not intentionally using that feature, leave storage unmapped and back up the tenant data directory on the runtime host.
Tenant operations
Day-two admin work- Tenant edit page is for ownership, plan changes, infrastructure overrides, and primary record maintenance.
- Tenant instance page is for day-two operations such as health review, uptime visibility, backup creation, restart, and redeploy.
- Provisioning feedback allows readiness callbacks to close a running lifecycle job after the runtime reports back.
- Runtime metadata stores base URL, launch URL, host port, latest failure details, and dependency selections.
When a tenant looks unhealthy, open the instance page before taking action. Review the latest health snapshot, uptime, active host, latest backup, and last provisioning job so you can choose the right response.
Scheduler and commands
Required automationThis is one of the most important parts of setup. If the scheduler is not running, new tenants stay queued, template installs wait, health checks stop, backups stop, and scheduled account actions do not run.
You do not create one cron job for each command below.
You create one server cron job that runs
php artisan schedule:run
every minute. Laravel then runs the correct commands automatically.
Find the app folder
Open the folder where N8Forge is installed. This is the folder that contains the
artisan file.
Test the scheduler once
Run php artisan schedule:run from that folder. If it works, you can
set
the server to run the same command automatically every minute.
Add one cron job on the server
In your server panel or cron manager, create a job that runs every minute. Replace the sample path below with your real N8Forge install path and PHP path.
* * * * * cd /path/to/n8forge && /usr/bin/php artisan schedule:run >> /dev/null 2>&1
If your host asks for separate fields, use:
- Frequency: every minute
- Working folder: the N8Forge folder that contains
artisan - Command:
/usr/bin/php artisan schedule:run
Confirm it is working
After a few minutes, create or update something that uses automation and confirm the related action moves forward. A newly created tenant should not remain stuck in queued for long if the scheduler is active.
Run the cron job on only one main app server unless you have intentionally designed a clustered scheduler setup.
| Command | Schedule | Purpose |
|---|---|---|
subscriptions:cancel-expired |
Every hour | Cancels expired subscriptions automatically |
saasforge:send-suspension-reminders |
Every hour | Sends scheduled suspension reminders |
saasforge:process-provisioning |
Every minute | Processes tenant create, restart, redeploy, suspend, and destroy jobs |
saasforge:check-runtime-health |
Every five minutes | Updates runtime health snapshots and instance status data |
saasforge:run-backups |
Daily | Creates tenant backups |
n8nforge:process-template-installs |
Every minute | Processes queued template installs for managed workspaces |
accounts:process-deletions |
Daily | Deletes accounts that reached the end of their deletion grace period |
activity:clean |
Daily | Removes old activity logs based on retention settings |
Create a test tenant. If the scheduler is working, provisioning should begin shortly
instead of sitting in queued.
The cron job is missing, using the wrong path, using the wrong PHP binary, or running in the wrong folder.
Send your hosting provider or server admin the cron command above and ask them to run it every minute on the N8Forge application server.
Backups and restore
Automated backups capture the full n8n data directory, including the SQLite database, credentials, and workflow files.
- Scope: The entire
/home/node/.n8ndirectory is tar'd, so the SQLite database is always included. - Schedule:
saasforge:run-backupsruns daily. Retention is configurable viaSAASFORGE_BACKUPS_MAX_PER_TENANT(default 7). - Manual backup: From the instance edit page at Automation → Managed Instances → Edit → Backup.
- Restore: From the instance view page (Automation → Managed Instances → {instance} → View), scroll to the Backups section and click Restore on a succeeded backup. The container stops, the data directory is replaced, and the container restarts.
- Restore status is shown as a badge on the backup card. All restore operations are logged to the activity log.
- SSH driver: Backup files are downloaded locally, then uploaded back to the remote server during restore. Both Docker and SSH drivers are supported.
Activity log
A unified activity log at SaaS → Activity Log shows all platform operations in reverse chronological order.
| Action type | Source | What it tracks |
|---|---|---|
saasforge_job_* |
Provisioning | Every job start, success, and failure |
n8nforge_template_* |
Templates | Template installs and failures |
saasforge_runtime_backup_* |
Backups | Backup queued, completed, or failed |
saasforge_runtime_restore_* |
Restore | Restore started, completed, or failed |
saasforge_runtime_health_* |
Health | Health check results |
saasforge_runtime_*_queued |
Actions | Restart, redeploy, or destroy queued |
Filter by action type or time period (today, yesterday, last 7/30 days). Error details are shown inline. Logs older than the configured retention period are cleaned automatically.
Without the every-minute scheduler cron job, provisioning, template delivery, reminders, cleanup, backups, health monitoring, and scheduled account actions will drift or stop.
Updates and deployment tooling
Release operations- Admin → Updates verifies the purchase code, checks for the latest release, and applies downloads when configured.
php artisan n8forge:updateruns pending update tasks for the current install.php artisan n8forge:update --downloaddownloads and applies the latest release before migrations and cache refresh.ops/scripts/build-mainfiles.shbuilds install and update packages.ops/scripts/push-demo.shhelps deploy the N8Forge app itself, not tenant runtimes.
The ops/ scripts are for deploying the N8Forge application. They are not a
replacement for SaaSForge provisioning servers or tenant runtime placement.
Maintenance workflow
Recommended routine- Daily review provisioning jobs, health checks, failed backups, and account notifications. Open the instance page for any degraded workspace.
- After plan changes verify billing IDs, role mapping, and provisioning, database, and storage assignments.
- After server changes confirm SSH credentials, Docker access, proxy paths, and capacity limits.
- After dependency changes validate one real tenant deployment before treating the change as complete.
- Before updates take backups and verify restore confidence for both the app and tenant runtime data.
Keep ACTIVITY_LOG_ENABLED active if you need an audit trail for runtime actions
such as health checks, backups, restart, or redeploy operations.
Troubleshooting
Common admin issuesCheck the active plan, subscription status, role mapping, and whether the observer, queue, and scheduler path is functioning.
Confirm saasforge:process-provisioning is scheduled and that the selected
driver, server, and optional runtime dependencies are valid.
Verify host, port, username, key or password, host-key policy, Docker access, remote paths, and whether the deploy user can write files and reload the reverse proxy.
Check that DNS points at the correct host, the tenant Caddy site file exists on that
provisioning server, Caddy reloaded successfully, and the primary domain matches the
n8n runtime base URL. There is no central wildcard ask-endpoint flow in
the current implementation.
Check credentials, SSL settings, S3 region formatting, enterprise licensing, and whether the plan or tenant still points at the intended dependency pool.
Open the tenant instance page first, review the last health result and active host, then decide between health check, backup, restart, or redeploy.