From Cloud Foundry to Kyma on SAP BTP: 5 Essential Migration Patterns

From Cloud Foundry to Kyma on SAP BTP: 5 Essential Migration Patterns

Introduction

This blog covers five core patterns you’ll need when moving from CF to Kyma, with working examples you can test on a BTP Trial Kyma cluster. Each pattern is presented as a direct comparison: here’s how you did it in Cloud Foundry, here’s how you do it in Kyma. The code examples are complete and tested—you can copy them into your own Kyma environment and see them work. By the end, you’ll have a practical understanding of the migration path and reference code you can adapt for your own applications.

What you’ll learn:

Basic deployment with APIRules (Istio-based routing)Service bindings using Kubernetes-native patternsPre-runtime configuration with init containersCredential Store integration for secrets managementDestination Service integration from Kyma

Prerequisites

Before starting, you’ll need:

SAP BTP Trial account with Kyma environment enabledkubectl CLI installed locallyBasic familiarity with Kubernetes concepts

Setup: Download Your Kubeconfig

First, let’s get connected to your Kyma cluster:

# Create directory structure
mkdir -p ~/kyma-tutorials
cd ~/kyma-tutorials

# Download kubeconfig from BTP Cockpit
# Navigate to: Subaccount → Kyma Environment → Download Kubeconfig
# Save to ~/.kube/config-kyma-trial

# Set kubeconfig
export KUBECONFIG=~/.kube/config-kyma-trial

# Verify connection
kubectl cluster-info

Critical first step: Enable Istio sidecar injection on your namespace. Kyma requires this for external routing to work:

# Create namespace
kubectl create namespace demo-app

# Enable Istio injection
kubectl label namespace demo-app istio-injection=enabled

# Set as default
kubectl config set-context –current –namespace=demo-app

Pattern 1: Deployment and Routing with APIRules

CF Pattern:

cf push myapp

Kyma Pattern: In Kyma, you need three resources: Deployment, Service, and APIRule.

apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-kyma
namespace: demo-app
spec:
replicas: 1
selector:
matchLabels:
app: hello-kyma
template:
metadata:
labels:
app: hello-kyma
spec:
containers:
– name: hello
image: hashicorp/http-echo:latest
args:
– “-text=Hello from Kyma!”
ports:
– containerPort: 5678

apiVersion: v1
kind: Service
metadata:
name: hello-kyma-service
namespace: demo-app
spec:
selector:
app: hello-kyma
ports:
– protocol: TCP
port: 80
targetPort: 5678

apiVersion: gateway.kyma-project.io/v2alpha1
kind: APIRule
metadata:
name: hello-kyma-api
namespace: demo-app
spec:
gateway: kyma-system/kyma-gateway
hosts:
– hello-kyma.<YOUR_CLUSTER_DOMAIN>
service:
name: hello-kyma-service
port: 80
rules:
– path: /*
methods: [“GET”]
noAuth: true

Deploy it:

kubectl apply -f deployment.yaml

# Wait for pod with Istio sidecar
kubectl get pods -n demo-app
# You should see 2/2 containers (app + istio-proxy)

# Test your app
curl https://hello-kyma.<YOUR_CLUSTER_DOMAIN>

Key differences:

CF Router → Istio + APIRuleCF routes → APIRule hostsAutomatic SSL in both, but APIRule uses noAuth: true vs App Router patterns

Pattern 2: Service Bindings – From VCAP_SERVICES to Kubernetes Secrets

CF Pattern:

cf create-service xsuaa application myxsuaa
cf bind-service myapp myxsuaa
# Credentials appear in VCAP_SERVICES environment variable

Kyma Pattern: In Kyma, service bindings create Kubernetes Secrets.

apiVersion: services.cloud.sap.com/v1
kind: ServiceInstance
metadata:
name: kyma-xsuaa
namespace: demo-app
spec:
serviceOfferingName: xsuaa
servicePlanName: application
parameters:
xsappname: kyma-demo-xsuaa
tenant-mode: dedicated
scopes:
– name: “$XSAPPNAME.Read”
description: “Read permission”
role-templates:
– name: Reader
scope-references:
– “$XSAPPNAME.Read”

apiVersion: services.cloud.sap.com/v1
kind: ServiceBinding
metadata:
name: kyma-xsuaa-binding
namespace: demo-app
spec:
serviceInstanceName: kyma-xsuaa
secretName: kyma-xsuaa-secret

Deploy it:

kubectl apply -f xsuaa-instance.yaml

# Wait for ready (takes 1-2 minutes)
kubectl get serviceinstance kyma-xsuaa -n demo-app -w

Now use the credentials in your app – you have two options:

Option 1: Environment Variables

env:
– name: XSUAA_CLIENTID
valueFrom:
secretKeyRef:
name: kyma-xsuaa-secret
key: clientid
– name: XSUAA_CLIENTSECRET
valueFrom:
secretKeyRef:
name: kyma-xsuaa-secret
key: clientsecret

Option 2: Volume Mounts

volumeMounts:
– name: xsuaa-volume
mountPath: /etc/secrets/xsuaa
readOnly: true
volumes:
– name: xsuaa-volume
secret:
secretName: kyma-xsuaa-secret

Then read credentials from files:

const fs = require(‘fs’);
const clientId = fs.readFileSync(‘/etc/secrets/xsuaa/clientid’, ‘utf8’);
const clientSecret = fs.readFileSync(‘/etc/secrets/xsuaa/clientsecret’, ‘utf8’);

Key differences:

Cloud Foundry Kyma

cf bind-serviceServiceBinding resourceVCAP_SERVICES JSONKubernetes SecretAuto-injected as env varMount as volume OR env varsApp parses JSONApp reads individual keys

Pattern 3: Pre-Runtime Configuration – From .profile to Init Containers

CF Pattern: Create a .profile script in your app directory:

#!/bin/bash
echo “Decrypting secrets…”
# Runs before app starts, in same container

Kyma Pattern: Use Kubernetes Init Containers that run before your main app:

apiVersion: apps/v1
kind: Deployment
metadata:
name: init-demo
namespace: demo-app
spec:
template:
spec:
# Init container runs FIRST
initContainers:
– name: decrypt-config
image: busybox:latest
command:
– sh
– -c
– |
echo “Init container running…”
# Decrypt secrets, download certs, etc.
cat /encrypted/config.txt | base64 -d > /decrypted/config.txt
echo “Runtime info: $(date)” >> /decrypted/runtime-info.txt
echo “Init complete!”
volumeMounts:
– name: encrypted-volume
mountPath: /encrypted
– name: decrypted-volume
mountPath: /decrypted

# Main app runs AFTER init completes
containers:
– name: app
image: myapp:latest
volumeMounts:
– name: decrypted-volume
mountPath: /decrypted
readOnly: true

volumes:
– name: encrypted-volume
configMap:
name: encrypted-config
– name: decrypted-volume
emptyDir: {}

Key differences:

CF .profile Kyma Init Containers

Shell script onlyAny container imageSame containerSeparate containerSequential onlyMultiple init containers (chained)No resource limitsCPU/memory limits per init

Real-world use cases:

Decrypt Credential Store secretsDownload certificates from external CARun database migrationsGenerate dynamic configurationWait for dependencies to be ready

Pattern 4: Credential Store Integration

In CF, you might fetch credentials in your .profile script. In Kyma, use an init container to fetch from Credential Store and write to a shared volume.

First, create the Credential Store service:

apiVersion: services.cloud.sap.com/v1
kind: ServiceInstance
metadata:
name: credstore
namespace: demo-app
spec:
serviceOfferingName: credstore
servicePlanName: trial

apiVersion: services.cloud.sap.com/v1
kind: ServiceBinding
metadata:
name: credstore-binding
namespace: demo-app
spec:
serviceInstanceName: credstore
secretName: credstore-secret

Then fetch credentials in an init container:

spec:
initContainers:
– name: fetch-credentials
image: curlimages/curl:latest
command:
– sh
– -c
– |
# Get OAuth token
OAUTH_URL=”$(cat /credstore/url | sed ‘s|/api.*||’)/oauth/token”
TOKEN=$(curl -s -X POST “$OAUTH_URL”
-d “grant_type=client_credentials”
-d “client_id=$(cat /credstore/username)”
-d “client_secret=$(cat /credstore/password)”
| jq -r ‘.access_token’)

# Fetch credential
CRED_URL=$(cat /credstore/url)
curl -s “$CRED_URL/password?name=database-password”
-H “Authorization: Bearer $TOKEN”
| jq -r ‘.value’ > /secrets/db-password
volumeMounts:
– name: credstore-creds
mountPath: /credstore
readOnly: true
– name: runtime-secrets
mountPath: /secrets

containers:
– name: app
image: myapp:latest
volumeMounts:
– name: runtime-secrets
mountPath: /secrets
readOnly: true

volumes:
– name: credstore-creds
secret:
secretName: credstore-secret
– name: runtime-secrets
emptyDir: {}

Why this matters:

Credentials NOT stored in Kubernetes Secrets (more secure)Credentials fetched at runtime (can rotate without redeploying)Main app doesn’t need Credential Store SDKInit container separates credential management from app logic

Pattern 5: Destination Service Integration

CF Pattern: In CF, you use the App Router with @sap/approuter package, which handles Destination Service integration automatically.

Kyma Pattern: In Kyma, you need to call the Destination Service API directly from your application.

First, create the destination in BTP Cockpit:

Go to Connectivity → DestinationsCreate destination:Name: backend-apiURL: https://api.example.comAuthentication: NoAuthenticationAdditional Property: forwardAuthToken = true

Then create the Destination service instance:

apiVersion: services.cloud.sap.com/v1
kind: ServiceInstance
metadata:
name: destination
namespace: demo-app
spec:
serviceOfferingName: destination
servicePlanName: lite

apiVersion: services.cloud.sap.com/v1
kind: ServiceBinding
metadata:
name: destination-binding
namespace: demo-app
spec:
serviceInstanceName: destination
secretName: destination-secret

Use it in your Node.js app:

const express = require(‘express’);
const axios = require(‘axios’);
const fs = require(‘fs’);
const app = express();

// Read Destination Service credentials from mounted secret
const destCreds = {
uri: fs.readFileSync(‘/etc/secrets/destination/uri’, ‘utf8’).trim(),
clientid: fs.readFileSync(‘/etc/secrets/destination/clientid’, ‘utf8’).trim(),
clientsecret: fs.readFileSync(‘/etc/secrets/destination/clientsecret’, ‘utf8’).trim(),
url: fs.readFileSync(‘/etc/secrets/destination/url’, ‘utf8’).trim()
};

app.get(‘/call-backend’, async (req, res) => {
try {
// 1. Get OAuth token for Destination Service
const tokenResponse = await axios.post(
`${destCreds.url}/oauth/token`,
new URLSearchParams({
grant_type: ‘client_credentials’,
client_id: destCreds.clientid,
client_secret: destCreds.clientsecret
}),
{ headers: { ‘Content-Type’: ‘application/x-www-form-urlencoded’ } }
);

const token = tokenResponse.data.access_token;

// 2. Get destination configuration
const destResponse = await axios.get(
`${destCreds.uri}/destination-configuration/v1/destinations/backend-api`,
{ headers: { Authorization: `Bearer ${token}` } }
);

const backendUrl = destResponse.data.destinationConfiguration.URL;

// 3. Call backend via destination
const backendResponse = await axios.get(`${backendUrl}/posts/1`);

res.json({
message: ‘Success!’,
data: backendResponse.data
});

} catch (error) {
res.status(500).json({ error: error.message });
}
});

app.listen(8080);

Deploy with the secret mounted:

apiVersion: apps/v1
kind: Deployment
metadata:
name: destination-demo
spec:
template:
spec:
containers:
– name: app
image: node:18-alpine
volumeMounts:
– name: destination-creds
mountPath: /etc/secrets/destination
readOnly: true
volumes:
– name: destination-creds
secret:
secretName: destination-secret

apiVersion: gateway.kyma-project.io/v2alpha1
kind: APIRule
metadata:
name: destination-demo-api
spec:
gateway: kyma-system/kyma-gateway
hosts:
– destination-demo.<YOUR_CLUSTER_DOMAIN>
service:
name: destination-demo-service
port: 80
rules:
– path: /*
methods: [“GET”]
noAuth: true

Key differences:

Cloud Foundry Kyma

App Router handles itManual API calls@sap/approuter packageCustom integration codeAuto-configured routesExplicit OAuth + API calls

Summary: CF vs Kyma Migration Checklist

Feature Cloud Foundry Kyma Equivalent

Deploycf pushkubectl apply -f deployment.yamlService Bindingcf bind-serviceServiceBinding resourceCredentialsVCAP_SERVICES JSON env varKubernetes Secret (volume/env)Pre-runtime.profile scriptInit ContainersRoutingCF Router + routesIstio + APIRuleAuthenticationApp RouterAPIRule accessStrategiesNamespace isolationCF Spaces (shared network)K8s Namespaces (network policies)Scalingcf scalekubectl scale or HPA

Key Takeaways

Istio is mandatory: Always enable istio-injection=enabled on namespaces before deploying apps that need external access.

Secrets are files, not JSON: In Kyma, service credentials are individual files in a mounted volume, not a single JSON object.

Init containers are powerful: They’re not just a .profile replacement—they can use any container image, have independent resource limits, and can be chained.

Manual integration required: Unlike CF’s App Router, Kyma requires you to integrate with BTP services (like Destination Service) directly via API calls.

Kubernetes-native: Kyma uses standard Kubernetes patterns. If you know Kubernetes, you know Kyma. If you don’t, learning Kyma teaches you portable Kubernetes skills.

Conclusion

The patterns in this blog give you a foundation to migrate existing CF apps to Kyma. Start with simple apps, master these five patterns, then tackle more complex workloads.

 

 

​ From Cloud Foundry to Kyma on SAP BTP: 5 Essential Migration PatternsIntroductionThis blog covers five core patterns you’ll need when moving from CF to Kyma, with working examples you can test on a BTP Trial Kyma cluster. Each pattern is presented as a direct comparison: here’s how you did it in Cloud Foundry, here’s how you do it in Kyma. The code examples are complete and tested—you can copy them into your own Kyma environment and see them work. By the end, you’ll have a practical understanding of the migration path and reference code you can adapt for your own applications.What you’ll learn:Basic deployment with APIRules (Istio-based routing)Service bindings using Kubernetes-native patternsPre-runtime configuration with init containersCredential Store integration for secrets managementDestination Service integration from KymaPrerequisitesBefore starting, you’ll need:SAP BTP Trial account with Kyma environment enabledkubectl CLI installed locallyBasic familiarity with Kubernetes conceptsSetup: Download Your KubeconfigFirst, let’s get connected to your Kyma cluster:# Create directory structure
mkdir -p ~/kyma-tutorials
cd ~/kyma-tutorials

# Download kubeconfig from BTP Cockpit
# Navigate to: Subaccount → Kyma Environment → Download Kubeconfig
# Save to ~/.kube/config-kyma-trial

# Set kubeconfig
export KUBECONFIG=~/.kube/config-kyma-trial

# Verify connection
kubectl cluster-infoCritical first step: Enable Istio sidecar injection on your namespace. Kyma requires this for external routing to work:# Create namespace
kubectl create namespace demo-app

# Enable Istio injection
kubectl label namespace demo-app istio-injection=enabled

# Set as default
kubectl config set-context –current –namespace=demo-appPattern 1: Deployment and Routing with APIRulesCF Pattern:cf push myappKyma Pattern: In Kyma, you need three resources: Deployment, Service, and APIRule.apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-kyma
namespace: demo-app
spec:
replicas: 1
selector:
matchLabels:
app: hello-kyma
template:
metadata:
labels:
app: hello-kyma
spec:
containers:
– name: hello
image: hashicorp/http-echo:latest
args:
– “-text=Hello from Kyma!”
ports:
– containerPort: 5678

apiVersion: v1
kind: Service
metadata:
name: hello-kyma-service
namespace: demo-app
spec:
selector:
app: hello-kyma
ports:
– protocol: TCP
port: 80
targetPort: 5678

apiVersion: gateway.kyma-project.io/v2alpha1
kind: APIRule
metadata:
name: hello-kyma-api
namespace: demo-app
spec:
gateway: kyma-system/kyma-gateway
hosts:
– hello-kyma.<YOUR_CLUSTER_DOMAIN>
service:
name: hello-kyma-service
port: 80
rules:
– path: /*
methods: [“GET”]
noAuth: trueDeploy it:kubectl apply -f deployment.yaml

# Wait for pod with Istio sidecar
kubectl get pods -n demo-app
# You should see 2/2 containers (app + istio-proxy)

# Test your app
curl https://hello-kyma.<YOUR_CLUSTER_DOMAIN>Key differences:CF Router → Istio + APIRuleCF routes → APIRule hostsAutomatic SSL in both, but APIRule uses noAuth: true vs App Router patternsPattern 2: Service Bindings – From VCAP_SERVICES to Kubernetes SecretsCF Pattern:cf create-service xsuaa application myxsuaa
cf bind-service myapp myxsuaa
# Credentials appear in VCAP_SERVICES environment variableKyma Pattern: In Kyma, service bindings create Kubernetes Secrets.apiVersion: services.cloud.sap.com/v1
kind: ServiceInstance
metadata:
name: kyma-xsuaa
namespace: demo-app
spec:
serviceOfferingName: xsuaa
servicePlanName: application
parameters:
xsappname: kyma-demo-xsuaa
tenant-mode: dedicated
scopes:
– name: “$XSAPPNAME.Read”
description: “Read permission”
role-templates:
– name: Reader
scope-references:
– “$XSAPPNAME.Read”

apiVersion: services.cloud.sap.com/v1
kind: ServiceBinding
metadata:
name: kyma-xsuaa-binding
namespace: demo-app
spec:
serviceInstanceName: kyma-xsuaa
secretName: kyma-xsuaa-secretDeploy it:kubectl apply -f xsuaa-instance.yaml

# Wait for ready (takes 1-2 minutes)
kubectl get serviceinstance kyma-xsuaa -n demo-app -wNow use the credentials in your app – you have two options:Option 1: Environment Variablesenv:
– name: XSUAA_CLIENTID
valueFrom:
secretKeyRef:
name: kyma-xsuaa-secret
key: clientid
– name: XSUAA_CLIENTSECRET
valueFrom:
secretKeyRef:
name: kyma-xsuaa-secret
key: clientsecretOption 2: Volume MountsvolumeMounts:
– name: xsuaa-volume
mountPath: /etc/secrets/xsuaa
readOnly: true
volumes:
– name: xsuaa-volume
secret:
secretName: kyma-xsuaa-secretThen read credentials from files:const fs = require(‘fs’);
const clientId = fs.readFileSync(‘/etc/secrets/xsuaa/clientid’, ‘utf8’);
const clientSecret = fs.readFileSync(‘/etc/secrets/xsuaa/clientsecret’, ‘utf8’);Key differences:Cloud Foundry Kymacf bind-serviceServiceBinding resourceVCAP_SERVICES JSONKubernetes SecretAuto-injected as env varMount as volume OR env varsApp parses JSONApp reads individual keysPattern 3: Pre-Runtime Configuration – From .profile to Init ContainersCF Pattern: Create a .profile script in your app directory:#!/bin/bash
echo “Decrypting secrets…”
# Runs before app starts, in same containerKyma Pattern: Use Kubernetes Init Containers that run before your main app:apiVersion: apps/v1
kind: Deployment
metadata:
name: init-demo
namespace: demo-app
spec:
template:
spec:
# Init container runs FIRST
initContainers:
– name: decrypt-config
image: busybox:latest
command:
– sh
– -c
– |
echo “Init container running…”
# Decrypt secrets, download certs, etc.
cat /encrypted/config.txt | base64 -d > /decrypted/config.txt
echo “Runtime info: $(date)” >> /decrypted/runtime-info.txt
echo “Init complete!”
volumeMounts:
– name: encrypted-volume
mountPath: /encrypted
– name: decrypted-volume
mountPath: /decrypted

# Main app runs AFTER init completes
containers:
– name: app
image: myapp:latest
volumeMounts:
– name: decrypted-volume
mountPath: /decrypted
readOnly: true

volumes:
– name: encrypted-volume
configMap:
name: encrypted-config
– name: decrypted-volume
emptyDir: {}Key differences:CF .profile Kyma Init ContainersShell script onlyAny container imageSame containerSeparate containerSequential onlyMultiple init containers (chained)No resource limitsCPU/memory limits per initReal-world use cases:Decrypt Credential Store secretsDownload certificates from external CARun database migrationsGenerate dynamic configurationWait for dependencies to be readyPattern 4: Credential Store IntegrationIn CF, you might fetch credentials in your .profile script. In Kyma, use an init container to fetch from Credential Store and write to a shared volume.First, create the Credential Store service:apiVersion: services.cloud.sap.com/v1
kind: ServiceInstance
metadata:
name: credstore
namespace: demo-app
spec:
serviceOfferingName: credstore
servicePlanName: trial

apiVersion: services.cloud.sap.com/v1
kind: ServiceBinding
metadata:
name: credstore-binding
namespace: demo-app
spec:
serviceInstanceName: credstore
secretName: credstore-secretThen fetch credentials in an init container:spec:
initContainers:
– name: fetch-credentials
image: curlimages/curl:latest
command:
– sh
– -c
– |
# Get OAuth token
OAUTH_URL=”$(cat /credstore/url | sed ‘s|/api.*||’)/oauth/token”
TOKEN=$(curl -s -X POST “$OAUTH_URL”
-d “grant_type=client_credentials”
-d “client_id=$(cat /credstore/username)”
-d “client_secret=$(cat /credstore/password)”
| jq -r ‘.access_token’)

# Fetch credential
CRED_URL=$(cat /credstore/url)
curl -s “$CRED_URL/password?name=database-password”
-H “Authorization: Bearer $TOKEN”
| jq -r ‘.value’ > /secrets/db-password
volumeMounts:
– name: credstore-creds
mountPath: /credstore
readOnly: true
– name: runtime-secrets
mountPath: /secrets

containers:
– name: app
image: myapp:latest
volumeMounts:
– name: runtime-secrets
mountPath: /secrets
readOnly: true

volumes:
– name: credstore-creds
secret:
secretName: credstore-secret
– name: runtime-secrets
emptyDir: {}Why this matters:Credentials NOT stored in Kubernetes Secrets (more secure)Credentials fetched at runtime (can rotate without redeploying)Main app doesn’t need Credential Store SDKInit container separates credential management from app logicPattern 5: Destination Service IntegrationCF Pattern: In CF, you use the App Router with @sap/approuter package, which handles Destination Service integration automatically.Kyma Pattern: In Kyma, you need to call the Destination Service API directly from your application.First, create the destination in BTP Cockpit:Go to Connectivity → DestinationsCreate destination:Name: backend-apiURL: https://api.example.comAuthentication: NoAuthenticationAdditional Property: forwardAuthToken = trueThen create the Destination service instance:apiVersion: services.cloud.sap.com/v1
kind: ServiceInstance
metadata:
name: destination
namespace: demo-app
spec:
serviceOfferingName: destination
servicePlanName: lite

apiVersion: services.cloud.sap.com/v1
kind: ServiceBinding
metadata:
name: destination-binding
namespace: demo-app
spec:
serviceInstanceName: destination
secretName: destination-secretUse it in your Node.js app:const express = require(‘express’);
const axios = require(‘axios’);
const fs = require(‘fs’);
const app = express();

// Read Destination Service credentials from mounted secret
const destCreds = {
uri: fs.readFileSync(‘/etc/secrets/destination/uri’, ‘utf8’).trim(),
clientid: fs.readFileSync(‘/etc/secrets/destination/clientid’, ‘utf8’).trim(),
clientsecret: fs.readFileSync(‘/etc/secrets/destination/clientsecret’, ‘utf8’).trim(),
url: fs.readFileSync(‘/etc/secrets/destination/url’, ‘utf8’).trim()
};

app.get(‘/call-backend’, async (req, res) => {
try {
// 1. Get OAuth token for Destination Service
const tokenResponse = await axios.post(
`${destCreds.url}/oauth/token`,
new URLSearchParams({
grant_type: ‘client_credentials’,
client_id: destCreds.clientid,
client_secret: destCreds.clientsecret
}),
{ headers: { ‘Content-Type’: ‘application/x-www-form-urlencoded’ } }
);

const token = tokenResponse.data.access_token;

// 2. Get destination configuration
const destResponse = await axios.get(
`${destCreds.uri}/destination-configuration/v1/destinations/backend-api`,
{ headers: { Authorization: `Bearer ${token}` } }
);

const backendUrl = destResponse.data.destinationConfiguration.URL;

// 3. Call backend via destination
const backendResponse = await axios.get(`${backendUrl}/posts/1`);

res.json({
message: ‘Success!’,
data: backendResponse.data
});

} catch (error) {
res.status(500).json({ error: error.message });
}
});

app.listen(8080);Deploy with the secret mounted:apiVersion: apps/v1
kind: Deployment
metadata:
name: destination-demo
spec:
template:
spec:
containers:
– name: app
image: node:18-alpine
volumeMounts:
– name: destination-creds
mountPath: /etc/secrets/destination
readOnly: true
volumes:
– name: destination-creds
secret:
secretName: destination-secret

apiVersion: gateway.kyma-project.io/v2alpha1
kind: APIRule
metadata:
name: destination-demo-api
spec:
gateway: kyma-system/kyma-gateway
hosts:
– destination-demo.<YOUR_CLUSTER_DOMAIN>
service:
name: destination-demo-service
port: 80
rules:
– path: /*
methods: [“GET”]
noAuth: trueKey differences:Cloud Foundry KymaApp Router handles itManual API calls@sap/approuter packageCustom integration codeAuto-configured routesExplicit OAuth + API callsSummary: CF vs Kyma Migration ChecklistFeature Cloud Foundry Kyma EquivalentDeploycf pushkubectl apply -f deployment.yamlService Bindingcf bind-serviceServiceBinding resourceCredentialsVCAP_SERVICES JSON env varKubernetes Secret (volume/env)Pre-runtime.profile scriptInit ContainersRoutingCF Router + routesIstio + APIRuleAuthenticationApp RouterAPIRule accessStrategiesNamespace isolationCF Spaces (shared network)K8s Namespaces (network policies)Scalingcf scalekubectl scale or HPAKey TakeawaysIstio is mandatory: Always enable istio-injection=enabled on namespaces before deploying apps that need external access.Secrets are files, not JSON: In Kyma, service credentials are individual files in a mounted volume, not a single JSON object.Init containers are powerful: They’re not just a .profile replacement—they can use any container image, have independent resource limits, and can be chained.Manual integration required: Unlike CF’s App Router, Kyma requires you to integrate with BTP services (like Destination Service) directly via API calls.Kubernetes-native: Kyma uses standard Kubernetes patterns. If you know Kubernetes, you know Kyma. If you don’t, learning Kyma teaches you portable Kubernetes skills.ConclusionThe patterns in this blog give you a foundation to migrate existing CF apps to Kyma. Start with simple apps, master these five patterns, then tackle more complex workloads.    Read More Technology Blog Posts by Members articles 

#SAP

#SAPTechnologyblog

You May Also Like

More From Author