Written by
Simon Daniel Moser (smoser@de.ibm.com) - Distinguished Engineer for Containers @ IBM Cloud
Enrico Regge (reggeenr@de.ibm.com) - Software Architect @ IBM Cloud
----------------------------------
Some time ago, we published a blog post on how to connect your Code Engine applications to various backends over private connections using IBM Cloud Satellite Connector. While Satellite Connectors remain a flexible, all‑purpose solution, some use cases can now be addressed with simpler, more targeted options. In particular, when you need private connectivity within IBM Cloud’s VPC network, the new private path capability offers a much easier setup. In this article, we revisit the example from the earlier blog and adapt it to private path, giving you a straightforward way to compare both approaches and choose the one that best fits your requirements.
Let’s recap: To understand the challenge of connecting an Application from Code Engine to somewhere over the private network, recall that Code Engine itself runs on IBM Cloud’s Virtual Private Cloud (VPC) infrastructure. Because it is a fully managed service—designed specifically to spare customers from operating their own infrastructure—the underlying VPC is controlled by the Code Engine team rather than by the customer. Normally, if you wanted to connect one VPC to another, you would create VPE gateways in both the source and target VPC and link them. But since customers do not own the VPC in which their Code Engine apps run, and cannot create their own VPEs there for security reasons, they appear to have no straightforward way to establish private connectivity from their application to other destinations.
In oder to illustrate this, we’ll be using this example: As a user, I want to access my database backend on TCP port XYZ from workload deployed on Code Engine, without exposing my VPC to the public internet. Figure 1 depicts the architecture of the sample:
- On the left hand side, you see the above mentioned, Code Engine owned VPC that host your application or job (green box)
- On the right hand side, you see your own VPC, running a Virtual Server with a Postgres DB installed on it that you want to connect to (dark blue box)
- The light blue and red boxes depict the private path components that you need to create in order to make them talk to each other
- Private Path VPE Gateways: Gateways created in the consumer VPC to connect to a Private Path service. Each consuming VPC must create a separate gateway for every Private Path service instance it needs to reach.
- Private Path Service: Defines the service configuration, including endpoints, policies, and the network load balancer used to handle inbound traffic.
- Private Path NLB: The network load balancer that receives and routes all inbound requests sent to the Private Path service endpoints.
- Policies: Control how connection requests are handled at provisioning time. Policies can auto‑approve, deny, or require review of requests from consuming accounts. These policies apply only during provisioning. After a connection is established, the consuming VPC can send traffic through the Private Path service regardless of the policy settings.
With the architecture in place, we’ll now walk through the example: first deploying all needed VPC components including the VSI that hosts the PostgreSQL instance, then adding the Private Path service and connecting it to the Code Engine project, and finally confirming that app and the job can connect to the database.
For those, who want to skip the manual setup and skip forward to a running example, please checkout the sample code that we’ve published at https://github.com/IBM/CodeEngine/tree/main/private-path-to-vpc-vsi. To ease setup, feel free to use the “run“ script that we published along with the sample source code, of the app and the job.
- Install jq. On MacOS, you can use following brew formulae to do a brew install jq.
- Install and configure the IBM Cloud CLI
- Install the IBM Cloud CLI including the Code Engine plugin as described in the Code Engine documentation
- Perform all necessary login steps to IBM Cloud
- Target the IBM Cloud region of your choice
- REGION=eu-es
- ibmcloud target -r $REGION
- Create the Virtual Private Cloud (VPC) that will host the Private Path and the VSI
- vpc_name=private-path-sample-vpc
- ibmcloud is vpc-create $vpc_name
- Create a Public Gateway, a Subnet, a Network ACL and a Security Group
- ibmcloud is public-gateway-create ${vpc_name}-gateway $vpc_name $REGION-1
- ibmcloud is network-acl-create ${vpc_name}-acl $vpc_name --rules '[{ "name": "egress", "action": "allow", "destination": "0.0.0.0/0", "direction": "outbound", "source": "0.0.0.0/0", "protocol": "all" }, { "name": "ingress", "action": "allow", "destination": "0.0.0.0/0", "direction": "inbound", "source": "0.0.0.0/0", "protocol": "all" }]'
- ibmcloud is subnet-create ${vpc_name}-subnet $vpc_name --zone $REGION-1 --ipv4-address-count 16 --pgw ${vpc_name}-gateway --acl ${vpc_name}-acl
- ibmcloud is security-group-create ${vpc_name}-group $vpc_name
- ibmcloud is security-group-rule-add ${vpc_name}-group outbound tcp --port-min 443 --port-max 443 --vpc $vpc_name
- ibmcloud is security-group-rule-add ${vpc_name}-group outbound udp --port-min 53 --port-max 53 --vpc $vpc_name
- ibmcloud is security-group-rule-add ${vpc_name}-group outbound all --remote 166.9.0.0/16 --vpc $vpc_name
- ibmcloud is security-group-rule-add ${vpc_name}-group outbound all --remote 161.26.0.0/16 --vpc $vpc_name
- Next, create a file on your local workstation, called “userdata-vsi-originserver.sh“
-
#!/bin/bash
touch /tmp/init_started
# ==========================
# nginx installation
# ==========================
dnf -y install nginx
rm -f /usr/share/nginx/html/index.html
echo "Hello world from `hostname`" > /usr/share/nginx/html/index.html
chmod go+r /usr/share/nginx/html/index.html
systemctl enable nginx
systemctl start nginx
systemctl status nginx
touch /tmp/nginx_done
# ==========================
# PostgreSQL installation
# ==========================
yum install postgresql-server postgresql-contrib -y
postgresql-setup initdb
systemctl start postgresql
systemctl enable postgresql
echo "host all all 0.0.0.0/0 md5" >> /var/lib/pgsql/data/pg_hba.conf
echo "listen_addresses = '*'" >> /var/lib/pgsql/data/postgresql.conf
sudo systemctl restart postgresql
# ==========================
# PostgreSQL init
# ==========================
useradd dbuser
sudo -i -u postgres bash << EOF
createuser dbuser
createdb guestbookdb -O dbuser
psql -c "ALTER USER dbuser PASSWORD 'myPassw0rd!';"
EOF
touch /tmp/postgresql_done
touch /tmp/init_done
- Now, that all the required components are in place, we can go ahead and create a VSI instance
- ibmcloud is instance-create ${vpc_name}-originserver $vpc_name $REGION-1 cx2-2x4 ${vpc_name}-subnet \
--image ibm-centos-stream-10-amd64-5 \
--boot-volume "{\"name\": \"boot-vol-attachment-name\", \"volume\": {\"name\": \"${vpc_name}-originserver-boot-vol\", \"capacity\": 100, \"profile\": {\"name\": \"general-purpose\"}}, \"delete_volume_on_instance_delete\": true}" \
--host-failure-policy restart \
--primary-network-interface "{\"name\": \"eth0\", \"allow_ip_spoofing\": false, \"auto_delete\": true, \"subnet\": {\"name\":\"${vpc_name}-subnet\"}, \"primary_ip\": {\"auto_delete\": true}, \"security_groups\": [{\"name\": \"${vpc_name}-group\"}]}" \
--user-data @userdata-vsi-originserver.sh
- Create the Private Path network load balancer, it pool and listener. To learn more about this configuration step, please see https://cloud.ibm.com/docs/vpc?topic=vpc-ppnlb-ui-creating-private-path-network-load-balancer&interface=cli
- ibmcloud is load-balancer-create ${vpc_name}-ppnlb private-path --family network --subnet ${vpc_name}-subnet
- ibmcloud is load-balancer-pool-create ${vpc_name}-ppnlb-pg-pool ${vpc_name}-ppnlb weighted_round_robin tcp 10 2 5 tcp --health-monitor-port 80
- ibmcloud is load-balancer-pool-member-create ${vpc_name}-ppnlb ${vpc_name}-ppnlb-pg-pool 5432 ${vpc_name}-originserver
- ppnlb_pg_pool_id=$(ibmcloud is load-balancer-pool ${vpc_name}-ppnlb ${vpc_name}-ppnlb-pg-pool --output JSON | jq -r '.id')
- ibmcloud is load-balancer-listener-create ${vpc_name}-ppnlb --port-min 5432 --port-max 5432 --protocol tcp --default-pool $ppnlb_pg_pool_id
- Create the Private Path service including the service endpoint, which is a FQDN, that is used to connect into the VPC. To learn more about this configuration step, please see https://cloud.ibm.com/docs/vpc?topic=vpc-private-path-service-about&interface=cli
- random_chars=$(openssl rand -hex 6)
- pps_service_endpoint="my-db.ce-${random_chars}.intra"
- ibmcloud is private-path-service-gateway-create --name ${vpc_name}-pps --default-access-policy permit --zonal-affinity true --service-endpoints $pps_service_endpoint --load-balancer ${vpc_name}-ppnlb
- To expose the Private Service to other accounts (e.g. the Code Engine service account) make sure to publish it
- ibmcloud is private-path-service-gateway-publish ${vpc_name}-pps
- Before heading over to Code Engine, it is important that you obtain the CRN of the Private Path service as this is needed in later setup steps. The CRN value looks similar to this one: “crn:v1:bluemix:public:is:eu-es:a/7658687ea07db8396963ebe2b8e1897d::private-path-service-gateway:r050-29977fba-f00c-4bbc-b0a5-659b7941094c“
- ibmcloud is private-path-service-gateway ${vpc_name}-pps
- Create or select the Code Engine project of your choice; e.g. ibmcloud ce project create --name private-path-sample or ibmcloud ce project select --name <yourProjectName>
- Create a the VPE Gateway Connection in the context of the Code Engine project to enable this project to connect to the Private Path service.
- ibmcloud ce connectivity outbound create --name my-private-path-connection --format private_path_service_gateway --pps-crn <your-private-path-crn>
- Observe the initialization of the Private Path service gateway
- ibmcloud ce connectivity outbound get --name my-private-path-connection
- Once it reached the status “ready”, it lists information such as the "Endpoints", listed in the “Private path service” section of the output shown below. From within the project, your workload can access the Private Path network load balancer by issuing requests towards these endpoints.
Getting allowed outbound destination 'my-private-path-connection'...
OK
Project name: private-path-sample
Name: my-private-path-connection
Format: private_path_service_gateway
Private path service:
CRN: crn:v1:bluemix:public:is:eu-es:a/7658687ea07db8396963ebe2b8e1897d::private-path-service-gateway:r050-29977fba-f00c-4bbc-b0a5-659b7941094c
Status: ready
Name: private-path-sample-vpc-pps
Endpoints:
my-db.ce-813513fa4c04.intra
VPE gateway:
Name: code-engine-prod-eu-es-53fdba
Code Engine account ID: 0d852dce0bc442ddb2831ffae1a04e31
Creation timestamp: 2025-12-15T09:56:22Z
IP addresses:
192.168.70.83
192.168.166.68
192.168.102.77
- Create a Code Engine secret, that contains connectivity information, such as the Private Path service endpoint, and credentials to authenticate in the database
- ibmcloud ce secret create --name pg-credentials --format generic \
--from-literal PGHOST=$pps_service_endpoint \
--from-literal PGPORT=5432 \
--from-literal PGUSER=dbuser \
--from-literal PGPASSWORD=myPassw0rd! \
--from-literal PGDATABASE=guestbookdb
- Create a Code Engine job, which will insert a random entry into the database on each job run instance execution
- ibmcloud ce job create --name my-ce-job\
--image icr.io/codeengine/ce-to-private-path/job \
--env-from-secret pg-credentials \
--memory 0.5G \
--cpu 0.25
- Submit a jobrun with 10 instances, to fill the database with some content
- ibmcloud ce jobrun submit --job my-ce-job --array-size 10 --wait
- Create a Code Engine app, that can be used to query the database entries
- ibmcloud ce app create --name my-ce-app \
--image icr.io/codeengine/ce-to-private-path/app \
--env-from-secret pg-credentials \
--memory 0.5G \
--cpu 0.25
Now, everything is in place. Let us quickly walk through the most important parts of our solution:
- The VPC VSI that hosts the custom database
- The Private Path network load balancer, which defines one or many backend pools that are distributing incoming requests to backends, such as VSIs
- The Private Path service that exposes the network load balancer through the service endpoints “my-db.ce-813513fa4c04.intra“
- The Private Path connection in the Code Engine project, making sure that this Code Engine project can connect to the Private Path service
Finally, let us open the application URL and see whether the application can read entries from the database:
To create more entries in the database, simply submit another job run:
ibmcloud ce jobrun submit --job my-ce-job --array-size 10 --wait
Congratulations — you’ve successfully established a private connection to your database using Private Path. Before we move on, here are two important technical clarifications. First, since Private Path enables a connection from a multi‑tenant service like Code Engine into your VPC, you might wonder whether this opens the door to unwanted or malicious traffic. It does not. In principle, at runtime Code Engine isolates its tenants and makes sure that only those projects that are connected with a certain private path can actually reach it. Furthermore, Code Engine only allows to setup such a connection from a Code Engine project that resides either in the same IBM Cloud account as the target VPC or within the same Enterprise Account Family. Projects outside of those boundaries can neither connect with your VPC, nor can they reach your VPC through Private Path, ensuring that your environment remains safe and fully under your control.
Secondly, review the account policies you configured in the sample. For simplicity, we allowed all inbound connections in this example, but that is not what you would do in a real production environment. In practice, we recommend setting the default policy for connection requests to “Deny all requests”. For the specific connections you want to permit, you can mark them as “Review all requests,” which introduces an approval step for added scrutiny, or as “Permit all requests” if you prefer to allow them automatically without the review process.
Conclusion and Outlook
In this article, we demonstrated Private Path, a VPC‑native option for connecting a Code Engine application to a backend over the private network, reducing exposure and supporting compliance requirements. The benefits of Private Path, which can also be used with other IBM Cloud services, include a simpler setup compared to Satellite Connectors or full VPE gateway configurations, and the fact that it uses native VPC networking. This gives network administrators better visibility and control, unlike Satellite Connector, which operates as an encrypted tunnel with limited insight. Private Path is also fully managed, whereas Satellite Connector tunnels require operational maintenance by the user.
However, both connection methods have their place. Private Path is best when you need private connectivity within IBM Cloud’s internal network. When both your workload and the target service
a) reside in IBM Cloud VPC, and that includes scenarios where you connect to on-premises through e.g. a VPC and the Direct Link service,
b) you cannot create your own VPE, and
c) you want a straightforward, low‑overhead setup without deploying intermediate components.
Satellite Connector is recommended when you need private connectivity from IBM Cloud to on‑premises systems, other clouds, or external network locations. It is also suitable when you want a single, consistent mechanism to link multiple environments and are comfortable managing a Connector instance.
In this blog post, as well as in the Satellite Connector blog post, we used a VSI as the target for the connection. However, that example represents only a small part of what both integration patterns can support. The figure below shows how Private Path can connect to: