Connecting SAP CAP to a Private PostgreSQL Database
SAP BTP Connectivity • SAP Cloud Connector • TCP/SOCKS5 • CAP Node.js
A practical, end-to-end guide for securely connecting an SAP CAP Node.js application on SAP BTP Cloud Foundry to a PostgreSQL database hosted in a private network or virtual private cloud.
What this guide delivers
Outcome By the end of this guide, both the CAP server and the generated PostgreSQL deployer can deploy and access a CDS model in a private PostgreSQL database through SAP BTP Connectivity and SAP Cloud Connector—without making the database publicly accessible. |
Applies to: SAP BTP Cloud Foundry environment, SAP Cloud Connector, CAP Node.js, PostgreSQL, TCP connectivity, and JWT-authenticated SOCKS5.
The problem
In one of my SAP CAP Node.js projects, the application was running on SAP BTP Cloud Foundry while PostgreSQL was hosted inside a private network. A standard PostgreSQL connection could not reach the database because the endpoint was intentionally unavailable on the public internet.
The solution was to combine an SAP Cloud Connector TCP mapping, the SAP BTP Connectivity service, an SAP BTP Destination, and a PostgreSQL adapter that performs the required JWT-authenticated SOCKS5 handshake before opening the PostgreSQL protocol connection.
This article walks through the complete implementation: Cloud Connector registration, TCP access control, Destination properties, CAP configuration, MTA deployment, local development, connection recovery, error handling, and final database verification.
npm package used in this guide
Visit cap-postgres-btp-connectivity on npm Author and community package disclosure: I developed and maintain |
Solution architecture
| CAP server / PostgreSQL deployer | → | SAP BTP Connectivity SOCKS5 proxy | → | SAP Cloud Connector TCP mapping | → | Private PostgreSQL database |
The SAP BTP Destination stores the PostgreSQL credentials and Cloud Connector virtual endpoint. The Connectivity service supplies the OAuth credentials and SOCKS5 proxy endpoint. SAP Cloud Connector maps the virtual host and port to the actual private PostgreSQL host and port.
Prerequisites
- An SAP BTP subaccount with a Cloud Foundry organization and space.
- Entitlements and service instances for Destination and Connectivity.
- SAP Cloud Connector installed on a host that can reach the PostgreSQL server.
- A PostgreSQL database, database name, port, and least-privilege technical user.
- Node.js 20 or later, CAP development tools, Cloud Foundry CLI, and MTA build tools.
- Permissions to configure Cloud Connector, Destinations, service bindings, and deployments.
Official references
- Inbound Connectivity
- Using the TCP Protocol for Cloud Applications
- Configure Access Control (TCP)
- CAP Hybrid Testing and cds bind
- npm package: cap-postgres-btp-connectivity
1. Install SAP Cloud Connector and Connect It to the BTP Subaccount
Download and install SAP Cloud Connector
Before configuring the SAP BTP subaccount, download and install SAP Cloud Connector by following the official SAP installation guide.
Install Cloud Connector on a host inside the private network—or in an approved network zone—that meets both connectivity requirements:
- It can directly reach the private PostgreSQL host and database port.
- It has outbound HTTPS access to the SAP BTP region host, either directly or through the organization’s HTTPS proxy.
The Cloud Connector host does not need an inbound connection from the public internet. It establishes the secure outbound tunnel to SAP BTP and exposes only the internal systems and ports explicitly allowed in Access Control.
Choose the installation procedure for the operating system:
For production, use the installer variant so Cloud Connector can run as a Windows service or Linux daemon and restart automatically after a system reboot. The portable variant is intended for non-production or developer scenarios.
Complete the initial setup
Start the Cloud Connector service and open its administration UI:
https://<CLOUD-CONNECTOR-HOST>:8443 |
If the browser runs on the same machine, use https://localhost:8443. Port 8443 is the default; use the port selected during installation if it was changed. During the first sign-in, complete the required initial password change and select the appropriate master installation option for the landscape.
figure 1 — Add a subaccount in SAP Cloud Connector Administration.
Figure 2 — Configure an outbound HTTPS proxy only when required by the network.
Choose a registration method
Cloud Connector offers manual registration and registration using authentication data. Authentication data avoids entering the subaccount password in Cloud Connector and is the preferred walkthrough below. Manual registration is also shown because it can be useful when authentication-data download is unavailable.
Figure 3 — Choose manual configuration or authentication-data configuration.
Option A — Use authentication data
- In SAP BTP cockpit, open the subaccount and navigate to Connectivity → Cloud Connectors.
- Select Download Authentication Data and store the downloaded file securely.
- In Cloud Connector, choose Configure using authentication data, browse to the file, and continue.
- Review the resolved Region, Subaccount, and Display Name. Enter a Location ID when the subaccount uses one, then select Finish.
Figure 4 — Download authentication data from the SAP BTP subaccount.
Figure 5 — Select the downloaded authentication-data file.
Figure 6 — Confirm the selected authentication-data file.
Figure 7 — Review the subaccount details and assign the Location ID.
Security: Treat the authentication-data file as sensitive, short-lived onboarding material. Do not commit it to source control or distribute it through chat or email. |
Verify the subaccount tunnel
After registration, Cloud Connector should report Connected and show a valid subaccount certificate. A message stating that no active resources are available is expected until the TCP system mapping is created in the next section.
Figure 8 — Successful subaccount connection before access-control resources are configured.
Option B — Configure manually
For manual registration, enter the region host, subaccount identifier, login email, password, display name, and Location ID, then select Finish. Use an account authorized for Cloud Connector registration and follow your organization’s credential-handling policy.
Figure 9 — Manual subaccount registration fields.
Confirm the connector in SAP BTP cockpit
Return to Connectivity → Cloud Connectors in the BTP cockpit. The active connector and its Location ID should be visible. Select it and open Exposed Back-End Systems; the list is initially empty.
Figure 10 — Active Cloud Connector displayed in the BTP cockpit.
2. Configure the PostgreSQL TCP mapping
In SAP Cloud Connector, select the registered subaccount, open Cloud to On-Premises → Access Control, and add a new system mapping.
Figure 11 — Add a virtual-to-internal system mapping.
- Select Non-SAP System as the back-end type.
- Select TCP as the protocol. Limit access to trusted applications and expose only the required database endpoint.
- Enter the internal PostgreSQL host and port. These are the actual private-network values reachable from the Cloud Connector host.
- Enter a virtual host and virtual port. Applications use these virtual values; do not expose the physical host in the Destination.
- Enable Check Internal Host, review the summary, and finish the wizard.
Figure 12 — Select Non-SAP System.
Figure 13 — Select TCP and acknowledge the security implications.
Figure 14 — Enter the physical PostgreSQL host and port reachable from Cloud Connector.
Figure 15 — Define the cloud-side virtual host and virtual port.
Figure 16 — Review the mapping and enable the internal-host reachability check.
Important naming constraint: For this implementation, keep both the Location ID and virtual host to a maximum of six lowercase alphanumeric characters to avoid truncation or mismatched routing values. Use the exact same Location ID and virtual endpoint consistently in Cloud Connector, the Destination, and CAP configuration. |
Verify both sides
The Access Control list should show a green status and Reachable. In the BTP cockpit, the connector should list the same virtual host as an Available TCP Non-SAP System. Do not continue until both checks succeed.
Figure 17 — Cloud Connector reports the PostgreSQL mapping as reachable.
Figure 18 — SAP BTP cockpit reports the exposed TCP back end as available.
3. Prepare the CAP application
Create or use a CAP Node.js project with a CDS data model and service definition. The sample shown below defines Employees and Departments and exposes them through EmployeeService. The specific entities are illustrative; the connectivity setup works with any valid CAP model supported by @cap-js/postgres.
Figure 19 — Example CAP CDS persistence model.
Figure 20 — Example CAP service exposing the model.
Install the adapter
npm install cap-postgres-btp-connectivityThe package declares compatible @cap-js/postgres, @SAP-cloud-sdk/connectivity, and pg peer dependencies. With a current npm client, compatible peers are installed automatically.
Configure the CAP database service
Add the adapter under cds.requires.db in package.json. No dbType property is required; this package is PostgreSQL-only.
{
"cds": {
"requires": {
"db": {
"kind": "postgres",
"impl": "cap-postgres-btp-connectivity",
"destination": "POSTGRES_DB_CONFIG",
"cloudConnectorLocationId": "db",
"logLevel": "info",
"logRepeatIntervalMillis": 10000,
"pool": {
"min": 1,
"max": 10,
"testOnBorrow": true,
"acquireTimeoutMillis": 60000,
"idleTimeoutMillis": 30000
}
}
}
}
}For local development, add localPort only when the SSH port-forward does not use the default 3004. Production uses the SOCKS5 proxy host and port from the Connectivity service binding.
4. Create the required SAP BTP Destination
Create a Destination named POSTGRES_DB_CONFIG, or use the exact name configured in cds.requires.db.destination. Every field below is required by this solution.
Section | Field | Required value |
Main | Name | POSTGRES_DB_CONFIG |
Main | Type | HTTP |
Main | Proxy Type | Internet |
Main | URL | |
Authentication | Authentication | BasicAuthentication |
Authentication | User | PostgreSQL database user |
Authentication | Password | PostgreSQL database password |
Additional | host | Cloud Connector virtual PostgreSQL host |
Additional | dbname | PostgreSQL database name |
Additional | port | Cloud Connector virtual PostgreSQL port |
Additional | cloudConnectorLocationId | Exact Cloud Connector Location ID |
Routing rule: The host and port Additional Properties must contain the Cloud Connector virtual endpoint—not the Azure/private PostgreSQL address. Property names are case-sensitive for the standard configuration. |
Figure 21 — Destination main properties and PostgreSQL credentials.
Figure 22 — Mandatory Destination Additional Properties for virtual routing.
Why Proxy Type is Internet
This implementation uses the Destination service to resolve configuration and credentials. It performs the TCP routing separately through the Connectivity SOCKS5 proxy, so the Destination itself is not used as an HTTP OnPremise route. The placeholder URL satisfies the HTTP Destination structure; the adapter reads host, port, dbname, and cloudConnectorLocationId from Additional Properties.
5. Configure MTA deployment
CAP generates the PostgreSQL deployer in gen/pg. Because this is an isolated Node.js module, install the adapter into gen/pg immediately after cds build.
build-parameters:
before-all:
- builder: custom
commands:
- npm ci
- npx cds build --production
- npm install cap-postgres-btp-connectivity --prefix gen/pgThe deployer must be bound to both Connectivity and Destination because it opens the same private PostgreSQL connection while deploying CDS artifacts.
- name: <APP-NAME>-postgres-deployer
type: nodejs
path: gen/pg
requires:
- name: <APP-NAME>-connectivity-service
- name: <APP-NAME>-destination-service
parameters:
build-parameters: null
buildpack: nodejs_buildpack
memory: 1024M
no-route: true
no-start: true
tasks:
- command: npm start
memory: 1024M
name: deploy-to-postgresqlNo application-owned adapter or SOCKS5 files need to be copied into gen/pg or gen/srv. The npm package supplies that implementation. Replace all placeholder module and resource names with the names used by your MTA descriptor.
Deploy
mbt build
cf deploy mta_archives/<generated-mtar-file>.mtarA successful deployment shows the CAP server as a running application. The PostgreSQL deployer module is intentionally stopped because no-start is enabled; its deployment task runs npm start only long enough to deploy the database model.
Figure 23 — CAP server and stopped PostgreSQL deployer module after deployment.
Verify deployment logs
Inspect the PostgreSQL deployer task or application logs. Confirm that the adapter reports connection.create.succeeded and that the deployer reports a successful deployment. A normal shutdown of the pooled PostgreSQL client after the task completes is expected.
Figure 24 — Successful PostgreSQL connection and deployment log sequence.
Verify the generated database objects
Connect to PostgreSQL using an authorized administration tool and confirm that the CAP-generated tables exist. In this example, the Employees and Departments entities appear alongside CAP framework tables and reused common-model tables.
Figure 25 — CAP-generated tables visible in pgAdmin.
6. Local Development Through Cloud Foundry SSH
A local CAP application requires two separate components:
- Connectivity and Destination service credentials.
- A network tunnel from the local machine to the SAP BTP Connectivity SOCKS5 proxy.
The service bindings provide credentials, while the Cloud Foundry SSH tunnel provides the network path. Both are required.
6.1 Bind Connectivity and Destination Locally
The recommended approach is cds bind. It resolves service credentials from Cloud Foundry without storing the resolved secrets directly in the project.
Option A — Inherit Bindings from the Deployed CAP Application
npx cds bind --to-app-services <CAP-SRV-APP>The equivalent short command is:
npx cds bind -a <CAP-SRV-APP>Option B — Bind the Service Instances Directly
If the CAP application has not yet been deployed, bind the Connectivity and Destination service instances individually:
npx cds bind connectivity --to <CONNECTIVITY-SERVICE-INSTANCE>
npx cds bind destination --to <DESTINATION-SERVICE-INSTANCE>These commands store binding metadata under the hybrid profile. CAP resolves the actual credentials when the application starts.
Option C — Use default-env.json for Temporary Local Testing
If the project already uses default-env.json, copy the required Connectivity and Destination entries from the deployed application’s VCAP_SERVICES environment.
View the deployed application environment with:
cf env <CAP-SRV-APP>Use the following structure:
{
"VCAP_SERVICES": {
"connectivity": [
{
"credentials": {
"...": "Connectivity binding credentials"
}
}
],
"destination": [
{
"credentials": {
"...": "Destination binding credentials"
}
}
]
}
}Security warning: default-env.json contains sensitive credentials and is a legacy local-binding approach. Never commit it, upload it, include it in an MTA archive, or share it. Add the file to .gitignore and prefer cds bind whenever possible.
6.2 Prepare the Cloud Foundry SSH Host
Cloud Foundry cannot establish an SSH connection directly to a managed Connectivity service instance. The tunnel must pass through a running Cloud Foundry application in the same space.
The SSH host can be:
- The deployed CAP server application.
- Another running application bound to the Connectivity service.
- A small temporary host application deployed specifically for SSH forwarding.
A dedicated SSH host requires only the Connectivity service binding:
cf bind-service <SSH-HOST-APP> <CONNECTIVITY-SERVICE-INSTANCE>
cf restage <SSH-HOST-APP>6.3 Enable SSH for the Application
First, verify that SSH is permitted for the Cloud Foundry space and enabled for the host application:
cf space-ssh-allowed
cf ssh-enabled <SSH-HOST-APP>If SSH is disabled and your account has the necessary permission, enable it and restage the application:
cf enable-ssh <SSH-HOST-APP>
cf restage <SSH-HOST-APP>Confirm that the host application has at least one running instance before opening the tunnel.
6.4 Resolve the Connectivity SOCKS5 Endpoint
When using cds bind, resolve the SOCKS5 proxy host and port:
npx cds env get requires.connectivity.credentials.onpremise_proxy_host --profile hybrid --resolve-bindings
npx cds env get requires.connectivity.credentials.onpremise_socks5_proxy_port --profile hybrid --resolve-bindingsUse these values as <ONPREMISE_PROXY_HOST> and <ONPREMISE_SOCKS5_PROXY_PORT> in the SSH command below.
6.5 Run the Tunnel and CAP Application in Two Terminals
Terminal 1 — Start the SSH Port-Forward
cf ssh <SSH-HOST-APP> -i 0 -L 3004:<ONPREMISE_PROXY_HOST>:<ONPREMISE_SOCKS5_PROXY_PORT> -N-i 0selects application instance 0.-Lmaps local port 3004 to the Connectivity SOCKS5 proxy.-Nopens only the port-forward without starting a remote shell.
Keep Terminal 1 open for the complete local development session. Closing it stops the network tunnel.
Terminal 2 — Start the Local CAP Application
When using cds bind:
cds watch --profile hybridWhen using default-env.json:
cds watchThe package connects locally to 127.0.0.1:3004. The SSH tunnel forwards that traffic to SAP BTP Connectivity, which then routes it through SAP Cloud Connector to the private PostgreSQL database.
6.6 Use a Different Local Port
If port 3004 is already occupied, use another local port—for example, 4010:
cf ssh <SSH-HOST-APP> -i 0 -L 4010:<ONPREMISE_PROXY_HOST>:<ONPREMISE_SOCKS5_PROXY_PORT> -NConfigure the same port in the CAP database configuration:
{
"localPort": 4010
}Important: Changing localPort affects local development only. In production, the package uses onpremise_socks5_proxy_port from the Connectivity service binding.
7. Operations, recovery, and troubleshooting
The adapter emits structured JSON logs for configuration, Destination lookup, token acquisition, SOCKS5 phases, PostgreSQL lifecycle, and pool validation. Sensitive values are redacted. logLevel defaults to info, and identical recurring failures are suppressed for 10 seconds by default to avoid log flooding.
Error code | Meaning | Corrective action |
SERVICE_BINDING_MISSING | Connectivity is absent from VCAP_SERVICES. | Bind Connectivity to both the CAP server and PostgreSQL deployer; for local use, resolve the hybrid bindings. |
DESTINATION_NOT_FOUND / DESTINATION_PROPERTIES_MISSING | Destination cannot be resolved or lacks required fields. | Verify the exact Destination name and all mandatory properties. |
SOCKS5_PROXY_CONNECT_FAILED | The application cannot reach the Connectivity SOCKS5 endpoint. | Check the service binding, proxy host/port, application binding, and local SSH tunnel. |
SOCKS5_AUTHENTICATION_FAILED | JWT authentication or Location ID does not match. | Check the Connectivity binding, subaccount, and exact Location ID. |
SOCKS5_TARGET_CONNECT_FAILED | Cloud Connector rejected or could not reach the virtual target. | Check TCP mapping, virtual host/port, Location ID, access control, and database availability. |
POSTGRES_CONNECT_FAILED | SOCKS5 succeeded but PostgreSQL login/protocol failed. | Check credentials, database name, TLS settings, pg_hba.conf, and database health. |
Connection recovery
testOnBorrow defaults to true. When an idle connection has ended or emitted an error, the pool discards it and creates a new SOCKS5 tunnel and PostgreSQL connection for a subsequent acquisition. This is connection replacement—not automatic SQL replay. A query interrupted by a connection failure is returned to the caller as an error. Never blindly retry writes; use transactions and idempotency controls.
Security checklist
- Expose only the required PostgreSQL host and port in Cloud Connector.
- Restrict Cloud Connector access to trusted applications and subaccounts.
- Use a least-privilege database user and rotate credentials regularly.
- Enable PostgreSQL TLS when required by the network and security design.
- Never commit VCAP_SERVICES, service keys, authentication-data files, Destination exports, or passwords.
- Redact tenant, user, host, and credential information before publishing screenshots.
Conclusion
The configuration shown here gives both the CAP server and the generated PostgreSQL deployer a consistent route to a private PostgreSQL database. SAP Cloud Connector controls the exposed TCP resource, SAP BTP Connectivity provides the authenticated SOCKS5 path, the Destination holds the database connection metadata, and cap-postgres-btp-connectivity integrates that path with the CAP PostgreSQL service lifecycle.
For production adoption, complement this setup with high availability, monitoring, credential rotation, TLS validation, capacity testing, and an application-level strategy for safely handling interrupted operations.