The git host sends the webhook to https://<your domain>/hooks/<app>.
Your reverse proxy forwards it to nimdeploy, which only listens locally.
nimdeploy checks it, in this order:
the signature (HMAC SHA-256 for GitHub, Gitea, Forgejo and Bitbucket;
the secret token for GitLab) against that deploy's secret. Wrong or
missing: 401, nothing else happens;
the repository must be the configured one;
the branch must be the configured one; tag pushes and branch
deletions are ignored;
duplicates (same delivery ID, e.g. Redeliver) are ignored.
It answers at once (202) so the git host never times out, and runs
the deploy in the background.
It runs your command as the user nimdeploy runs as, in its
working_directory, with the push details in the environment. The command
is run directly (no shell) unless you ask for one.
It records the result: a log file per run with the full output,
latest.log, status.json, an entry in nimdeploy history, a line in the
journal and, if configured, a notification and a commit status.
One run per deploy (lock = true). Different deploys run in parallel.
Latest push wins
A push during a run is queued; later pushes replace the queued one, so after a burst only the newest commit is deployed (queue = true). With queue = false it gets 409.
Exact commit
The script gets the pushed commit in DEPLOY_COMMIT, not "whatever the branch has now".
Timeout
30 minutes by default. On timeout the whole process group gets SIGTERM, then SIGKILL 10 s later, so no orphan builds keep running.
No retries
A failed run is not repeated by itself: fix and push again, or nimdeploy run.
Clean environment
Every secret named in the config is removed from the script's environment.
nimdeploy itself is a single Go process that idles at a few MB of memory and
no CPU between webhooks. What costs memory is your build (npm run build,
composer install), which runs as a child process: size the server for that.