Introduction
This guide walks through migrating a Node.js application secured with XSUAA from SAP Cloud Foundry to SAP Kyma on SAP BTP.
Cloud Foundry support in SAP BTP trial accounts has become increasingly limited. The ‘cf push’ capability is no longer available, and quotas are restricted, making it difficult to deploy meaningful applications. Given these constraints, I’ve found it more useful to deploy to Kyma instead. Unlike Cloud Foundry’s fully managed approach, Kyma offers Kubernetes-native deployment with direct control over your containers, networking, and scaling. This guide walks through migrating a Cloud Foundry application to Kyma on BTP trial.
Prerequisites
SAP BTP subaccount with Kyma enabledDocker installed (with buildx support for cross-platform builds)kubectl configured for your Kyma clusterAn existing XSUAA service instance on BTP with valid credentialsNode.js application using Express and XSUAA authentication
Step 1: Prepare Your Node.js Application
“name”: “xsuaa-demo”,
“version”: “1.0.0”,
“dependencies”: {
“express”: “^4.18.0”,
“axios”: “^1.6.0”,
“@sap/xsenv”: “^3.4.0”
}
}
const axios = require(‘axios’);
const xsenv = require(‘@sap/xsenv’);
const app = express();
const PORT = process.env.PORT || 3000;
let xsuaaService;
try {
const services = xsenv.getServices({ xsuaa: { tag: ‘xsuaa’ } });
xsuaaService = services.xsuaa;
console.log(‘XSUAA service loaded’);
} catch (err) {
console.error(‘Failed to load XSUAA:’, err.message);
process.exit(1);
}
async function getAccessToken() {
const response = await axios.post(xsuaaService.url, ‘grant_type=client_credentials&response_type=token’, {
auth: { username: xsuaaService.clientid, password: xsuaaService.clientsecret },
headers: { ‘Content-Type’: ‘application/x-www-form-urlencoded’ }
});
return response.data.access_token;
}
app.get(‘/health’, (req, res) => {
res.json({ status: ‘ok’ });
});
app.get(‘/token’, async (req, res) => {
try {
const token = await getAccessToken();
res.json({ access_token: token, token_type: ‘bearer’ });
} catch (err) {
res.status(401).json({ error: ‘Failed to get token’ });
}
});
app.listen(PORT, () => {
console.log(‘Server running on port ‘ + PORT);
});
Step 2: Create Dockerfile
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install –production
COPY . .
EXPOSE 3000
CMD [“node”, “server.js”]
Step 3: Build and Push Image
Step 4: Deploy to Kyma
kubectl expose deployment xsuaa-demo –port=3000 –type=LoadBalancer -n default
Step 5: Configure XSUAA Credentials
–from-literal=VCAP_SERVICES='{“xsuaa”:[{“credentials”:{“clientid”:”sb-xsuaa-demo!tXXXXXX”,”clientsecret”:”xxxxxxxx-xxxx…”,”url”:”https://mysubaccount.authentication.us10.hana.ondemand.com/oauth/token”},”label”:”xsuaa”,”tag”:”xsuaa”}]}’
Step 6: Set Environment Variables
kubectl rollout restart deployment/xsuaa-demo -n default
Step 7: Verify and Test
kubectl get svc xsuaa-demo -n default
EXTERNAL_IP=$(kubectl get svc xsuaa-demo -n default -o jsonpath='{.status.loadBalancer.ingress[0].hostname}’)
curl http://$EXTERNAL_IP:3000/health
curl http://$EXTERNAL_IP:3000/token
Key Differences: Cloud Foundry vs Kyma
Aspect Cloud Foundry Kyma (Kubernetes) Runtime ManagementFully managed by CFManaged by Kubernetes operatorsService BindingAutomatic via VCAP_SERVICESManual via Kubernetes secrets/env varsDeployment UnitApplication dropletContainer imageScalingCF managedKubernetes HPANetwork PoliciesLimitedFull Kubernetes NetworkPolicy support
Troubleshooting
1. Docker Build Issues on macOS
Error: “exec format error” in pod logs means you built for wrong architecture.
2. XSUAA Service Not Loading
Check for “Failed to load XSUAA service” in logs:
# Output should show credentials, not placeholder values
3. Token Request Fails with getaddrinfo ENOTFOUND
VCAP_SERVICES still contains placeholder values. Re-apply with actual credentials from BTP.
4. Pod Status Stuck on PodInitializing
If using Istio, disable sidecar injection:
kubectl rollout restart deployment/xsuaa-demo -n default
5. LoadBalancer Not Getting External IP
Use port-forward as alternative:
Conclusion
Migrating an XSUAA-secured application from SAP Cloud Foundry to Kyma provides greater flexibility while maintaining strong authentication standards. This approach enables seamless integration with other Kubernetes-based services on SAP BTP while leveraging XSUAA for secure API access.
Next Steps
Implement additional endpoints for your business logicAdd role-based access control (RBAC) using XSUAA scopesConfigure Kubernetes HPA for automatic scalingSet up monitoring and logging with Kyma’s observability stackImplement CI/CD pipelines using SAP Continuous Integration and Delivery
IntroductionThis guide walks through migrating a Node.js application secured with XSUAA from SAP Cloud Foundry to SAP Kyma on SAP BTP.Cloud Foundry support in SAP BTP trial accounts has become increasingly limited. The ‘cf push’ capability is no longer available, and quotas are restricted, making it difficult to deploy meaningful applications. Given these constraints, I’ve found it more useful to deploy to Kyma instead. Unlike Cloud Foundry’s fully managed approach, Kyma offers Kubernetes-native deployment with direct control over your containers, networking, and scaling. This guide walks through migrating a Cloud Foundry application to Kyma on BTP trial.PrerequisitesSAP BTP subaccount with Kyma enabledDocker installed (with buildx support for cross-platform builds)kubectl configured for your Kyma clusterAn existing XSUAA service instance on BTP with valid credentialsNode.js application using Express and XSUAA authenticationStep 1: Prepare Your Node.js Applicationpackage.json{
“name”: “xsuaa-demo”,
“version”: “1.0.0”,
“dependencies”: {
“express”: “^4.18.0”,
“axios”: “^1.6.0”,
“@sap/xsenv”: “^3.4.0”
}
}server.jsconst express = require(‘express’);
const axios = require(‘axios’);
const xsenv = require(‘@sap/xsenv’);
const app = express();
const PORT = process.env.PORT || 3000;
let xsuaaService;
try {
const services = xsenv.getServices({ xsuaa: { tag: ‘xsuaa’ } });
xsuaaService = services.xsuaa;
console.log(‘XSUAA service loaded’);
} catch (err) {
console.error(‘Failed to load XSUAA:’, err.message);
process.exit(1);
}
async function getAccessToken() {
const response = await axios.post(xsuaaService.url, ‘grant_type=client_credentials&response_type=token’, {
auth: { username: xsuaaService.clientid, password: xsuaaService.clientsecret },
headers: { ‘Content-Type’: ‘application/x-www-form-urlencoded’ }
});
return response.data.access_token;
}
app.get(‘/health’, (req, res) => {
res.json({ status: ‘ok’ });
});
app.get(‘/token’, async (req, res) => {
try {
const token = await getAccessToken();
res.json({ access_token: token, token_type: ‘bearer’ });
} catch (err) {
res.status(401).json({ error: ‘Failed to get token’ });
}
});
app.listen(PORT, () => {
console.log(‘Server running on port ‘ + PORT);
});Step 2: Create DockerfileDockerfileFROM node:18
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install –production
COPY . .
EXPOSE 3000
CMD [“node”, “server.js”]Step 3: Build and Push ImageImportant: When building on macOS (ARM64), always specify the target platform for amd64. docker buildx build –platform linux/amd64 -t your-registry/xsuaa-demo:latest –push .Step 4: Deploy to Kyma kubectl create deployment xsuaa-demo –image=your-registry/xsuaa-demo:latest -n default
kubectl expose deployment xsuaa-demo –port=3000 –type=LoadBalancer -n defaultStep 5: Configure XSUAA Credentials kubectl create secret generic vcap-services -n default
–from-literal=VCAP_SERVICES='{“xsuaa”:[{“credentials”:{“clientid”:”sb-xsuaa-demo!tXXXXXX”,”clientsecret”:”xxxxxxxx-xxxx…”,”url”:”https://mysubaccount.authentication.us10.hana.ondemand.com/oauth/token”},”label”:”xsuaa”,”tag”:”xsuaa”}]}’Step 6: Set Environment Variables kubectl set env deployment/xsuaa-demo VCAP_SERVICES='{“xsuaa”:[{“credentials”:{“clientid”:”sb-xsuaa-demo!tXXXXXX”,”clientsecret”:”xxxxxxxx-xxxx…”,”url”:”https://mysubaccount.authentication.us10.hana.ondemand.com/oauth/token”},”label”:”xsuaa”,”tag”:”xsuaa”}]}’ -n default
kubectl rollout restart deployment/xsuaa-demo -n defaultStep 7: Verify and Test kubectl logs -f deployment/xsuaa-demo -n default
kubectl get svc xsuaa-demo -n default
EXTERNAL_IP=$(kubectl get svc xsuaa-demo -n default -o jsonpath='{.status.loadBalancer.ingress[0].hostname}’)
curl http://$EXTERNAL_IP:3000/health
curl http://$EXTERNAL_IP:3000/tokenExpected Output: You should see “XSUAA service loaded” and “Server running on port 3000” in the logs.Key Differences: Cloud Foundry vs KymaAspect Cloud Foundry Kyma (Kubernetes) Runtime ManagementFully managed by CFManaged by Kubernetes operatorsService BindingAutomatic via VCAP_SERVICESManual via Kubernetes secrets/env varsDeployment UnitApplication dropletContainer imageScalingCF managedKubernetes HPANetwork PoliciesLimitedFull Kubernetes NetworkPolicy supportTroubleshooting1. Docker Build Issues on macOSError: “exec format error” in pod logs means you built for wrong architecture. docker buildx build –platform linux/amd64 -t your-registry/xsuaa-demo:latest –push .2. XSUAA Service Not LoadingCheck for “Failed to load XSUAA service” in logs: kubectl exec deployment/xsuaa-demo -n default — env | grep VCAP_SERVICES
# Output should show credentials, not placeholder values3. Token Request Fails with getaddrinfo ENOTFOUNDVCAP_SERVICES still contains placeholder values. Re-apply with actual credentials from BTP.4. Pod Status Stuck on PodInitializingIf using Istio, disable sidecar injection: kubectl patch deployment xsuaa-demo -n default -p ‘{“spec”:{“template”:{“metadata”:{“annotations”:{“sidecar.istio.io/inject”:”false”}}}}}’
kubectl rollout restart deployment/xsuaa-demo -n default5. LoadBalancer Not Getting External IPUse port-forward as alternative: kubectl port-forward svc/xsuaa-demo 3000:3000 -n defaultConclusionMigrating an XSUAA-secured application from SAP Cloud Foundry to Kyma provides greater flexibility while maintaining strong authentication standards. This approach enables seamless integration with other Kubernetes-based services on SAP BTP while leveraging XSUAA for secure API access.Next StepsImplement additional endpoints for your business logicAdd role-based access control (RBAC) using XSUAA scopesConfigure Kubernetes HPA for automatic scalingSet up monitoring and logging with Kyma’s observability stackImplement CI/CD pipelines using SAP Continuous Integration and Delivery Read More Technology Blog Posts by Members articles
#SAP
#SAPTechnologyblog