Architecture diagram: n8n on SAP BTP Cloud Foundry

If you have ever deployed something and whispered, "please work" before pressing Enter, this post is for you.

I wanted n8n running on SAP BTP Cloud Foundry with PostgreSQL, but I also wanted:

  • minimal setup
  • no credential circus in manifest files
  • one command that does the heavy lifting

And yes, we got all three.

The setup in one sentence

n8n + PostgreSQL on SAP BTP CF, deployed with a secure manifest and helper scripts that bootstrap services and secrets without committing sensitive data.

Why this approach is nice

The setup keeps your future self happy:

  • No DB credentials in manifest.yml
  • Runtime DB credentials come from VCAP_SERVICES
  • Temporary service key is created only when needed, then deleted
  • Local secret files stay gitignored
  • Deploy command stays beautifully boring

Boring deploys are elite deploys.

End-to-end flow

  1. Clone repository
  2. Login to Cloud Foundry
  3. Create PostgreSQL service instance (if missing)
  4. Create temporary service key and extract credentials
  5. Optionally bind service to an existing app
  6. Deploy to Cloud Foundry

Prerequisites

  • CF CLI v8+
  • Targeted org/space (cf target)

IMPORTANT: Make sure cf target points to the correct org and space before deployment, or your app may go somewhere surprising.

Command blocks

  • Login and target:
cf login --sso -a <api-endpoint>
cf target -o <org> -s <space>
  • Bootstrap and deploy:
npm run cf:bootstrap
  • Prepare only:
npm run cf:prepare
  • Deploy only:
npm run cf:deploy
  • Pull secrets to local env file:
npm run secrets:pull -- --service n8n-db --out .env.cf

Step 1: Login and target org/space

cf login --sso -a <api-endpoint>
cf target -o <org> -s <space>

Screenshot of subaccount and CF details:

Cloud Foundry API endpoint and org details

Step 2: One command to bootstrap and deploy

npm run cf:bootstrap

This runs:

  • npm run cf:prepare
  • npm run cf:deploy

IMPORTANT: Runtime DB credentials are read from VCAP_SERVICES after binding. Keep secrets out of manifest.yml.

At this point, you can feel 37% more DevOps already.

What cf:prepare does behind the scenes

  • ensures service instance n8n-db exists (creates if missing)
  • waits for service readiness
  • tries to read PostgreSQL creds from bound app VCAP_SERVICES
  • if unavailable, creates temporary service key and reads creds
  • writes vars.secrets.yml for local/debug use
  • deletes the temporary service key

Default waiting behavior:

  • waits up to 15 minutes
  • polls every 10 seconds

If your foundation is slower than Monday mornings:

node scripts/cf-prepare-deploy.mjs --service n8n-db --wait-minutes 30 --poll-seconds 15

If you require local secrets generation strictly:

node scripts/cf-prepare-deploy.mjs --service n8n-db --app n8n-appp --require-secrets

First-run screenshots (the fun part)

  • PostgreSQL service creation in progress:

PostgreSQL service creation in progress

  • n8n overview with first workflow visible:

n8n application overview

  • n8n first-time owner setup page:

n8n owner account setup

  • Workflow editor after successful setup:

n8n workflow editor and saved workflow

  • Logout/settings menu:

n8n logout and settings menu

  • Signed in n8n screen:

n8n sign in page

Deploy command used

cf push -f manifest.yml

Route behavior note:

manifest.yml uses random-route: true, so route collisions are avoided across accounts/spaces.

IMPORTANT: Keep random-route: true unless you intentionally manage a custom route.

Get your final route with:

cf app n8n-appp

Optional: bind service to an existing app

node scripts/cf-prepare-deploy.mjs --service n8n-db --bind-app <app-name>
cf restage <app-name>

Normally, deploy-time binding is already handled via services: in manifest.yml.

Useful commands cheat sheet

npm run cf:prepare
npm run cf:deploy
npm run secrets:pull -- --service n8n-db --out .env.cf

Security checklist

  • no DB secrets in manifest
  • local secret files are gitignored
  • temporary service keys are cleaned up
  • runtime credentials come from bound CF environment

Security and simplicity are not enemies. They are teammates.

If your service name is different

node scripts/cf-prepare-deploy.mjs --service <your-service-name> --offering postgresql-db --plan trial --secrets-file vars.secrets.yml

Final thoughts

Deploying n8n on SAP BTP Cloud Foundry felt like assembling IKEA furniture, except this time I actually had the manual and only one screw was left over.

If you are trying this setup and hit weird CF errors, do not panic. Most of the time it is one of:

  • wrong target org/space
  • service still provisioning
  • route naming collision
  • a tiny typo that somehow caused existential crisis

Happy shipping.