or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

admin-jmx.mdansi-support.mdaot-native-image.mdapplication-info.mdavailability.mdbootstrap.mdbootstrapping.mdbuilder.mdcloud-platform.mdconfiguration-annotations.mdconfiguration-data.mdconfiguration-properties.mdconversion.mddiagnostics.mdenvironment-property-sources.mdindex.mdjson-support.mdlifecycle-events.mdlogging.mdorigin-tracking.mdresource-loading.mdretry-support.mdssl-tls.mdstartup-metrics.mdsupport.mdsystem-utilities.mdtask-execution.mdthreading.mdutilities.mdvalidation.mdweb-support.md
tile.json

cloud-platform.mddocs/

Cloud Platform Detection

Quick Reference

This documentation has been enhanced for AI coding agents with comprehensive examples, complete API signatures, thread safety notes, error handling patterns, and production-ready usage patterns.

Package: org.springframework.boot.cloud Module: org.springframework.boot:spring-boot Since: 1.3.0

Spring Boot's cloud platform detection automatically identifies the cloud environment where an application is running and configures appropriate settings. This enables cloud-specific optimizations and feature toggling without code changes.

Overview

The cloud platform system provides:

  • CloudPlatform: Enum of supported cloud platforms with auto-detection
  • Platform-specific configuration: Automatic property source ordering
  • Environment detection: Identifies runtime platform from environment variables

Core Components

CloudPlatform

Enum representing supported cloud platforms with detection logic.

package org.springframework.boot.cloud;

import org.springframework.core.env.Environment;

/**
 * Simple detection for well known cloud platforms. Detection can be forced using the
 * "spring.main.cloud-platform" configuration property.
 *
 * Each platform has unique environment variable patterns for detection.
 *
 * @since 1.3.0
 */
public enum CloudPlatform {

    /**
     * No cloud platform detected.
     */
    NONE {
        @Override
        public boolean isDetected(Environment environment) {
            return false;
        }
    },

    /**
     * Cloud Foundry platform.
     * Detected via VCAP_APPLICATION or VCAP_SERVICES environment variables.
     */
    CLOUD_FOUNDRY {
        @Override
        public boolean isDetected(Environment environment) {
            return environment.containsProperty("VCAP_APPLICATION") ||
                   environment.containsProperty("VCAP_SERVICES");
        }
    },

    /**
     * Heroku platform.
     * Detected via DYNO environment variable.
     */
    HEROKU {
        @Override
        public boolean isDetected(Environment environment) {
            return environment.containsProperty("DYNO");
        }
    },

    /**
     * SAP Cloud Platform.
     * Detected via HC_LANDSCAPE environment variable.
     */
    SAP {
        @Override
        public boolean isDetected(Environment environment) {
            return environment.containsProperty("HC_LANDSCAPE");
        }
    },

    /**
     * Kubernetes platform.
     * Detected via KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT
     * environment variables.
     */
    KUBERNETES {
        @Override
        public boolean isDetected(Environment environment) {
            return environment.containsProperty("KUBERNETES_SERVICE_HOST") &&
                   environment.containsProperty("KUBERNETES_SERVICE_PORT");
        }
    },

    /**
     * Microsoft Azure platform.
     * Detected via WEBSITE_INSTANCE_ID environment variable (Azure App Service).
     *
     * @since 3.4.0
     */
    AZURE {
        @Override
        public boolean isDetected(Environment environment) {
            return environment.containsProperty("WEBSITE_INSTANCE_ID");
        }
    },

    /**
     * Amazon Web Services platform.
     * Detected via AWS_EXECUTION_ENV environment variable or specific Lambda
     * environment variables.
     *
     * @since 3.4.0
     */
    AMAZON_WEB_SERVICES {
        @Override
        public boolean isDetected(Environment environment) {
            return environment.containsProperty("AWS_EXECUTION_ENV") ||
                   environment.containsProperty("AWS_LAMBDA_FUNCTION_NAME");
        }
    };

    /**
     * Determines if the platform is detected in the environment.
     *
     * @param environment the environment to check
     * @return true if this platform is active
     */
    public abstract boolean isDetected(Environment environment);

    /**
     * Returns the active CloudPlatform or NONE if not running on a known platform.
     * Checks platforms in order: forced via property, then each platform's detection.
     *
     * @param environment the environment
     * @return the active platform (never null)
     */
    public static CloudPlatform getActive(Environment environment) {
        // First check for forced platform via spring.main.cloud-platform
        String forcedPlatform = environment.getProperty("spring.main.cloud-platform");
        if (forcedPlatform != null) {
            return valueOf(forcedPlatform.toUpperCase().replace('-', '_'));
        }

        // Auto-detect platform
        for (CloudPlatform platform : values()) {
            if (platform.isDetected(environment)) {
                return platform;
            }
        }

        return NONE;
    }

    /**
     * Returns true if the platform is active in the given environment.
     *
     * @param environment the environment
     * @return true if this platform is active
     */
    public boolean isActive(Environment environment) {
        return getActive(environment) == this;
    }

    /**
     * Returns true if running on any cloud platform (not NONE).
     *
     * @param environment the environment
     * @return true if running on a cloud platform
     * @since 3.0.0
     */
    public static boolean isCloudPlatform(Environment environment) {
        return getActive(environment) != NONE;
    }
}

CloudFoundryVcapEnvironmentPostProcessor

Environment post-processor that parses Cloud Foundry VCAP variables.

package org.springframework.boot.cloud;

import org.springframework.boot.EnvironmentPostProcessor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.logging.DeferredLogFactory;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;

/**
 * An EnvironmentPostProcessor that knows where to find VCAP (Cloud Foundry) metadata
 * in the existing environment. Parses VCAP_APPLICATION and VCAP_SERVICES metadata
 * and flattens it into properties that are easily consumed by Environment users.
 *
 * VCAP_APPLICATION is a shallow hash with basic application info (name, instance id, etc.)
 * VCAP_SERVICES is a hash of lists where keys are service labels and values are
 * lists of service instance metadata.
 *
 * Properties are flattened as:
 * - VCAP_APPLICATION -> vcap.application.*
 * - VCAP_SERVICES -> vcap.services.{service-name}.*
 *
 * Thread Safety: Thread-safe. Can be called concurrently.
 *
 * @since 1.3.0
 */
public class CloudFoundryVcapEnvironmentPostProcessor
        implements EnvironmentPostProcessor, Ordered {

    /**
     * Create a new CloudFoundryVcapEnvironmentPostProcessor instance.
     * @param logFactory the log factory to use
     * @since 3.0.0
     */
    public CloudFoundryVcapEnvironmentPostProcessor(DeferredLogFactory logFactory) {
    }

    /**
     * Set the order of this post-processor.
     * Default: Before ConfigDataEnvironmentPostProcessor (ORDER - 5).
     * @param order the order value
     */
    public void setOrder(int order) {
    }

    @Override
    public int getOrder() {
        // Returns ConfigDataEnvironmentPostProcessor.ORDER - 5
    }

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment,
                                      SpringApplication application) {
        // Parses VCAP_APPLICATION and VCAP_SERVICES if running on Cloud Foundry
        // Adds flattened properties to environment as "vcap" property source
    }
}

Example VCAP Properties:

# VCAP_APPLICATION properties
vcap.application.instance_id=2ce0ac627a6c8e47e936d829a3a47b5b
vcap.application.instance_index=0
vcap.application.name=myapp
vcap.application.uris[0]=myapp.cfapps.io
vcap.application.version=0138c4a6-2a73-416b-aca0-572c09f7ca53

# VCAP_SERVICES properties (flattened by service name)
vcap.services.mysql.name=mysql
vcap.services.mysql.label=rds-mysql-1.0
vcap.services.mysql.plan=10mb
vcap.services.mysql.credentials.hostname=mysql-service.aws.com
vcap.services.mysql.credentials.port=3306
vcap.services.mysql.credentials.username=dbuser
vcap.services.mysql.credentials.password=dbpass
vcap.services.mysql.credentials.name=mydb

Usage in Application:

@Component
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
public class CloudFoundryServiceBinder {

    @Value("${vcap.services.mysql.credentials.hostname}")
    private String dbHost;

    @Value("${vcap.services.mysql.credentials.port}")
    private int dbPort;

    @Value("${vcap.services.mysql.credentials.username}")
    private String dbUsername;

    @Value("${vcap.services.mysql.credentials.password}")
    private String dbPassword;

    @PostConstruct
    public void bindServices() {
        System.out.printf("Connecting to MySQL: %s:%d%n", dbHost, dbPort);
        // Configure DataSource with VCAP credentials
    }
}

Usage Examples

Detecting Cloud Platform

package com.example.config;

import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

/**
 * Component that detects and reports cloud platform.
 */
@Component
public class PlatformDetector {

    private final Environment environment;

    public PlatformDetector(Environment environment) {
        this.environment = environment;
    }

    public void detectPlatform() {
        CloudPlatform platform = CloudPlatform.getActive(environment);

        System.out.println("Running on: " + platform);

        switch (platform) {
            case KUBERNETES:
                System.out.println("Kubernetes detected - using pod-based configuration");
                break;

            case CLOUD_FOUNDRY:
                System.out.println("Cloud Foundry detected - using VCAP services");
                break;

            case HEROKU:
                System.out.println("Heroku detected - using dyno configuration");
                break;

            case AMAZON_WEB_SERVICES:
                System.out.println("AWS detected - using cloud-native features");
                break;

            case AZURE:
                System.out.println("Azure detected - using App Service configuration");
                break;

            case NONE:
                System.out.println("No cloud platform detected - using default configuration");
                break;
        }
    }

    public boolean isCloud() {
        return CloudPlatform.isCloudPlatform(environment);
    }

    public boolean isKubernetes() {
        return CloudPlatform.KUBERNETES.isActive(environment);
    }
}

Conditional Configuration

package com.example.config;

import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Platform-specific configuration beans.
 */
@Configuration
public class CloudPlatformConfig {

    /**
     * Kubernetes-specific configuration.
     */
    @Bean
    @ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
    public ServiceDiscovery kubernetesServiceDiscovery() {
        System.out.println("Configuring Kubernetes service discovery");
        return new KubernetesServiceDiscovery();
    }

    /**
     * Cloud Foundry-specific configuration.
     */
    @Bean
    @ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
    public ServiceDiscovery cloudFoundryServiceDiscovery() {
        System.out.println("Configuring Cloud Foundry service discovery");
        return new CloudFoundryServiceDiscovery();
    }

    /**
     * AWS-specific configuration.
     */
    @Bean
    @ConditionalOnCloudPlatform(CloudPlatform.AMAZON_WEB_SERVICES)
    public ServiceDiscovery awsServiceDiscovery() {
        System.out.println("Configuring AWS service discovery");
        return new AwsServiceDiscovery();
    }

    /**
     * Configuration for non-cloud environments.
     */
    @Bean
    @ConditionalOnCloudPlatform(CloudPlatform.NONE)
    public ServiceDiscovery localServiceDiscovery() {
        System.out.println("Configuring local service discovery");
        return new LocalServiceDiscovery();
    }
}

Forcing Platform Detection

# application.properties
# Force specific platform (useful for testing)
spring.main.cloud-platform=kubernetes

# Or disable cloud detection
spring.main.cloud-platform=none
# application.yml
spring:
  main:
    cloud-platform: kubernetes  # kubernetes, cloud-foundry, heroku, aws, azure, none

Platform-Specific Properties

package com.example.config;

import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

/**
 * Configuration that adapts based on cloud platform.
 */
@Component
@ConfigurationProperties(prefix = "app")
public class AdaptiveConfiguration {

    private final Environment environment;
    private String serviceUrl;
    private int timeout;

    public AdaptiveConfiguration(Environment environment) {
        this.environment = environment;
        adaptToPlatform();
    }

    private void adaptToPlatform() {
        CloudPlatform platform = CloudPlatform.getActive(environment);

        switch (platform) {
            case KUBERNETES:
                // Use service DNS names in Kubernetes
                serviceUrl = "http://backend-service:8080";
                timeout = 5000;
                break;

            case CLOUD_FOUNDRY:
                // Use VCAP_SERVICES in Cloud Foundry
                serviceUrl = resolveFromVcapServices();
                timeout = 10000;
                break;

            case HEROKU:
                // Use Heroku config vars
                serviceUrl = environment.getProperty("BACKEND_URL",
                    "https://backend.herokuapp.com");
                timeout = 30000;
                break;

            case AMAZON_WEB_SERVICES:
                // Use AWS service endpoints
                serviceUrl = resolveFromAwsServices();
                timeout = 5000;
                break;

            default:
                // Local development defaults
                serviceUrl = "http://localhost:8080";
                timeout = 30000;
        }
    }

    private String resolveFromVcapServices() {
        // Parse VCAP_SERVICES JSON
        return "http://backend-service";
    }

    private String resolveFromAwsServices() {
        // Use AWS service discovery
        return "http://backend.internal";
    }

    public String getServiceUrl() {
        return serviceUrl;
    }

    public int getTimeout() {
        return timeout;
    }
}

Health Indicator with Platform Info

package com.example.health;

import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

/**
 * Health indicator including cloud platform information.
 */
@Component
public class PlatformHealthIndicator implements HealthIndicator {

    private final Environment environment;

    public PlatformHealthIndicator(Environment environment) {
        this.environment = environment;
    }

    @Override
    public Health health() {
        CloudPlatform platform = CloudPlatform.getActive(environment);

        return Health.up()
            .withDetail("platform", platform.name())
            .withDetail("isCloud", CloudPlatform.isCloudPlatform(environment))
            .withDetail("environment", determineEnvironment(platform))
            .build();
    }

    private String determineEnvironment(CloudPlatform platform) {
        if (platform == CloudPlatform.NONE) {
            return "local";
        }
        return environment.getProperty("spring.profiles.active", "unknown");
    }
}

Feature Toggles Based on Platform

package com.example.features;

import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

/**
 * Service with platform-aware feature flags.
 */
@Service
public class FeatureToggleService {

    private final Environment environment;

    public FeatureToggleService(Environment environment) {
        this.environment = environment;
    }

    public boolean isDistributedTracingEnabled() {
        // Enable in cloud environments
        return CloudPlatform.isCloudPlatform(environment);
    }

    public boolean isServiceMeshEnabled() {
        // Only enable in Kubernetes
        return CloudPlatform.KUBERNETES.isActive(environment);
    }

    public boolean isAutoScalingEnabled() {
        CloudPlatform platform = CloudPlatform.getActive(environment);

        // Enable for cloud platforms that support auto-scaling
        return platform == CloudPlatform.KUBERNETES ||
               platform == CloudPlatform.CLOUD_FOUNDRY ||
               platform == CloudPlatform.AMAZON_WEB_SERVICES ||
               platform == CloudPlatform.AZURE;
    }

    public boolean useCloudLoadBalancer() {
        CloudPlatform platform = CloudPlatform.getActive(environment);

        // Use cloud load balancers instead of client-side
        return platform != CloudPlatform.NONE;
    }
}

Platform-Specific Environment Variables

Kubernetes

KUBERNETES_SERVICE_HOST=10.0.0.1
KUBERNETES_SERVICE_PORT=443

Cloud Foundry

VCAP_APPLICATION={"application_id":"..."}
VCAP_SERVICES={"mysql":[{"credentials":{...}}]}

Heroku

DYNO=web.1
PORT=5000

AWS

AWS_EXECUTION_ENV=AWS_Lambda_java11
AWS_LAMBDA_FUNCTION_NAME=my-function
AWS_REGION=us-east-1

Azure

WEBSITE_INSTANCE_ID=abcd1234
WEBSITE_SITE_NAME=myapp

Testing with Different Platforms

package com.example;

import org.junit.jupiter.api.Test;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;

import static org.assertj.core.api.Assertions.assertThat;

/**
 * Tests for cloud platform detection.
 */
@SpringBootTest
class CloudPlatformDetectionTest {

    @Autowired
    private Environment environment;

    @Test
    @TestPropertySource(properties = {
        "KUBERNETES_SERVICE_HOST=10.0.0.1",
        "KUBERNETES_SERVICE_PORT=443"
    })
    void detectsKubernetes() {
        CloudPlatform platform = CloudPlatform.getActive(environment);
        assertThat(platform).isEqualTo(CloudPlatform.KUBERNETES);
    }

    @Test
    @TestPropertySource(properties = {
        "spring.main.cloud-platform=cloud-foundry"
    })
    void forcesCloudFoundry() {
        CloudPlatform platform = CloudPlatform.getActive(environment);
        assertThat(platform).isEqualTo(CloudPlatform.CLOUD_FOUNDRY);
    }

    @Test
    void detectsNoneByDefault() {
        CloudPlatform platform = CloudPlatform.getActive(environment);
        assertThat(platform).isEqualTo(CloudPlatform.NONE);
    }
}

Common Patterns

Pattern 1: Multi-Cloud Configuration Manager

Complete configuration system that adapts to detected cloud platform:

package com.example.cloud;

import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Manages platform-specific configuration with fallback strategies.
 */
@Component
public class MultiCloudConfigManager {

    private final Environment environment;
    private final CloudPlatform platform;
    private final Map<String, String> configCache = new ConcurrentHashMap<>();

    public MultiCloudConfigManager(Environment environment) {
        this.environment = environment;
        this.platform = CloudPlatform.getActive(environment);
        initializeConfiguration();
    }

    private void initializeConfiguration() {
        System.out.println("Initializing configuration for platform: " + platform);

        switch (platform) {
            case KUBERNETES:
                loadKubernetesConfig();
                break;
            case CLOUD_FOUNDRY:
                loadCloudFoundryConfig();
                break;
            case AMAZON_WEB_SERVICES:
                loadAwsConfig();
                break;
            case AZURE:
                loadAzureConfig();
                break;
            case HEROKU:
                loadHerokuConfig();
                break;
            default:
                loadLocalConfig();
        }
    }

    private void loadKubernetesConfig() {
        // Service discovery via DNS
        configCache.put("service.discovery.type", "kubernetes-dns");
        configCache.put("service.backend.url",
            "http://backend-service.default.svc.cluster.local:8080");

        // ConfigMap and Secret paths
        String configPath = "/etc/config";
        String secretPath = "/etc/secrets";
        configCache.put("config.path", configPath);
        configCache.put("secret.path", secretPath);

        // Health check endpoints
        configCache.put("management.health.livenessState.enabled", "true");
        configCache.put("management.health.readinessState.enabled", "true");
    }

    private void loadCloudFoundryConfig() {
        // VCAP_SERVICES parsing
        String vcapServices = environment.getProperty("VCAP_SERVICES");
        if (vcapServices != null) {
            configCache.put("service.discovery.type", "cloud-foundry-vcap");
            parseVcapServices(vcapServices);
        }

        // Cloud Foundry specific settings
        configCache.put("server.port", environment.getProperty("PORT", "8080"));
        configCache.put("service.registry.url",
            environment.getProperty("EUREKA_URL", "http://eureka-server"));
    }

    private void loadAwsConfig() {
        // AWS service endpoints
        String region = environment.getProperty("AWS_REGION", "us-east-1");
        configCache.put("cloud.aws.region.static", region);
        configCache.put("service.discovery.type", "aws-service-discovery");

        // Lambda-specific configuration
        if (environment.containsProperty("AWS_LAMBDA_FUNCTION_NAME")) {
            configCache.put("spring.main.web-application-type", "none");
            configCache.put("logging.level.root", "INFO");
        }

        // ECS-specific configuration
        if (environment.containsProperty("AWS_EXECUTION_ENV")) {
            configCache.put("service.backend.url",
                "http://backend.internal:8080");
        }
    }

    private void loadAzureConfig() {
        // Azure App Service configuration
        String instanceId = environment.getProperty("WEBSITE_INSTANCE_ID");
        String siteName = environment.getProperty("WEBSITE_SITE_NAME");

        configCache.put("cloud.azure.instance.id", instanceId);
        configCache.put("cloud.azure.site.name", siteName);
        configCache.put("service.discovery.type", "azure-app-service");

        // Azure-specific endpoints
        configCache.put("service.backend.url",
            String.format("https://%s-backend.azurewebsites.net", siteName));
    }

    private void loadHerokuConfig() {
        // Heroku dyno configuration
        String dyno = environment.getProperty("DYNO");
        configCache.put("heroku.dyno", dyno);

        // Database URL from Heroku
        String databaseUrl = environment.getProperty("DATABASE_URL");
        if (databaseUrl != null) {
            configCache.put("spring.datasource.url", databaseUrl);
        }

        // Service URLs from config vars
        configCache.put("service.backend.url",
            environment.getProperty("BACKEND_URL", "http://localhost:8080"));
    }

    private void loadLocalConfig() {
        configCache.put("service.discovery.type", "local");
        configCache.put("service.backend.url", "http://localhost:8080");
        configCache.put("spring.datasource.url",
            "jdbc:postgresql://localhost:5432/devdb");
    }

    private void parseVcapServices(String vcapJson) {
        // Parse VCAP_SERVICES JSON and extract service bindings
        // Simplified for example - use proper JSON parsing in production
        configCache.put("vcap.parsed", "true");
    }

    public String getConfig(String key) {
        return configCache.getOrDefault(key,
            environment.getProperty(key));
    }

    public String getConfig(String key, String defaultValue) {
        return configCache.getOrDefault(key,
            environment.getProperty(key, defaultValue));
    }

    public CloudPlatform getPlatform() {
        return platform;
    }

    public boolean isCloud() {
        return platform != CloudPlatform.NONE;
    }

    public Map<String, String> getAllConfig() {
        return new HashMap<>(configCache);
    }
}

Pattern 2: Platform-Aware Service Discovery

Service discovery implementation that adapts to cloud platform:

package com.example.discovery;

import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * Platform-aware service discovery with caching and fallback.
 */
@Service
public class PlatformAwareServiceDiscovery {

    private final Environment environment;
    private final CloudPlatform platform;
    private final HttpClient httpClient;
    private final ConcurrentMap<String, ServiceEndpoint> cache;

    public PlatformAwareServiceDiscovery(Environment environment) {
        this.environment = environment;
        this.platform = CloudPlatform.getActive(environment);
        this.httpClient = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(5))
            .build();
        this.cache = new ConcurrentHashMap<>();

        System.out.println("Service discovery initialized for: " + platform);
    }

    public ServiceEndpoint discoverService(String serviceName) {
        // Check cache first
        ServiceEndpoint cached = cache.get(serviceName);
        if (cached != null && !cached.isExpired()) {
            return cached;
        }

        // Discover based on platform
        ServiceEndpoint endpoint = switch (platform) {
            case KUBERNETES -> discoverKubernetesService(serviceName);
            case CLOUD_FOUNDRY -> discoverCloudFoundryService(serviceName);
            case AMAZON_WEB_SERVICES -> discoverAwsService(serviceName);
            case AZURE -> discoverAzureService(serviceName);
            case HEROKU -> discoverHerokuService(serviceName);
            default -> discoverLocalService(serviceName);
        };

        // Cache the result
        if (endpoint != null) {
            cache.put(serviceName, endpoint);
        }

        return endpoint;
    }

    private ServiceEndpoint discoverKubernetesService(String serviceName) {
        // Kubernetes DNS-based service discovery
        String namespace = environment.getProperty("kubernetes.namespace", "default");
        String domain = environment.getProperty("kubernetes.cluster.domain",
            "svc.cluster.local");

        String url = String.format("http://%s.%s.%s", serviceName, namespace, domain);

        // Try to resolve the service
        try {
            HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url + "/actuator/health"))
                .timeout(Duration.ofSeconds(3))
                .GET()
                .build();

            HttpResponse<String> response = httpClient.send(request,
                HttpResponse.BodyHandlers.ofString());

            if (response.statusCode() == 200) {
                return new ServiceEndpoint(serviceName, url, platform,
                    Duration.ofMinutes(5));
            }
        } catch (Exception e) {
            System.err.println("Failed to discover Kubernetes service: " + e.getMessage());
        }

        return null;
    }

    private ServiceEndpoint discoverCloudFoundryService(String serviceName) {
        // Cloud Foundry service discovery via VCAP_SERVICES
        String vcapServices = environment.getProperty("VCAP_SERVICES");
        if (vcapServices != null) {
            // Parse VCAP_SERVICES and find service by name
            // Simplified - use proper JSON parsing in production
            String url = parseVcapServicesForUrl(vcapServices, serviceName);
            if (url != null) {
                return new ServiceEndpoint(serviceName, url, platform,
                    Duration.ofMinutes(10));
            }
        }

        // Fallback to environment variable
        String envUrl = environment.getProperty(serviceName.toUpperCase() + "_URL");
        if (envUrl != null) {
            return new ServiceEndpoint(serviceName, envUrl, platform,
                Duration.ofMinutes(10));
        }

        return null;
    }

    private ServiceEndpoint discoverAwsService(String serviceName) {
        // AWS service discovery via Cloud Map or ECS service discovery
        String region = environment.getProperty("AWS_REGION", "us-east-1");

        // Try ECS service discovery first
        String ecsServiceUrl = String.format("http://%s.internal:8080", serviceName);

        try {
            HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(ecsServiceUrl + "/actuator/health"))
                .timeout(Duration.ofSeconds(3))
                .GET()
                .build();

            HttpResponse<String> response = httpClient.send(request,
                HttpResponse.BodyHandlers.ofString());

            if (response.statusCode() == 200) {
                return new ServiceEndpoint(serviceName, ecsServiceUrl, platform,
                    Duration.ofMinutes(5));
            }
        } catch (Exception e) {
            System.err.println("Failed to discover AWS service: " + e.getMessage());
        }

        return null;
    }

    private ServiceEndpoint discoverAzureService(String serviceName) {
        // Azure App Service discovery
        String siteName = environment.getProperty("WEBSITE_SITE_NAME");
        String url = String.format("https://%s-%s.azurewebsites.net",
            siteName, serviceName);

        return new ServiceEndpoint(serviceName, url, platform,
            Duration.ofMinutes(10));
    }

    private ServiceEndpoint discoverHerokuService(String serviceName) {
        // Heroku service discovery via config vars
        String envVarName = serviceName.toUpperCase().replace("-", "_") + "_URL";
        String url = environment.getProperty(envVarName);

        if (url != null) {
            return new ServiceEndpoint(serviceName, url, platform,
                Duration.ofMinutes(10));
        }

        // Fallback to Heroku subdomain pattern
        url = String.format("https://%s.herokuapp.com", serviceName);
        return new ServiceEndpoint(serviceName, url, platform,
            Duration.ofMinutes(10));
    }

    private ServiceEndpoint discoverLocalService(String serviceName) {
        // Local development - use localhost
        int port = environment.getProperty(serviceName + ".port", Integer.class, 8080);
        String url = String.format("http://localhost:%d", port);

        return new ServiceEndpoint(serviceName, url, platform,
            Duration.ofMinutes(1));
    }

    private String parseVcapServicesForUrl(String vcapJson, String serviceName) {
        // Simplified JSON parsing - use Jackson or Gson in production
        return null;
    }

    public void invalidateCache(String serviceName) {
        cache.remove(serviceName);
    }

    public void clearCache() {
        cache.clear();
    }

    public static class ServiceEndpoint {
        private final String name;
        private final String url;
        private final CloudPlatform platform;
        private final long expiresAt;

        public ServiceEndpoint(String name, String url, CloudPlatform platform,
                              Duration ttl) {
            this.name = name;
            this.url = url;
            this.platform = platform;
            this.expiresAt = System.currentTimeMillis() + ttl.toMillis();
        }

        public String getName() { return name; }
        public String getUrl() { return url; }
        public CloudPlatform getPlatform() { return platform; }

        public boolean isExpired() {
            return System.currentTimeMillis() > expiresAt;
        }

        @Override
        public String toString() {
            return String.format("ServiceEndpoint{name='%s', url='%s', platform=%s}",
                name, url, platform);
        }
    }
}

Pattern 3: Cloud Platform Migration Helper

Tool for testing application behavior across different platforms:

package com.example.migration;

import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.context.annotation.Profile;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
 * Helper for simulating different cloud platforms during testing/migration.
 */
@Component
@Profile({"test", "migration"})
public class CloudPlatformMigrationHelper {

    private final ConfigurableEnvironment environment;

    public CloudPlatformMigrationHelper(ConfigurableEnvironment environment) {
        this.environment = environment;
    }

    public void simulateKubernetes() {
        Map<String, Object> kubernetesProps = new HashMap<>();
        kubernetesProps.put("KUBERNETES_SERVICE_HOST", "10.0.0.1");
        kubernetesProps.put("KUBERNETES_SERVICE_PORT", "443");

        applyPlatformProperties("kubernetes-simulation", kubernetesProps);
        verifyPlatform(CloudPlatform.KUBERNETES);
    }

    public void simulateCloudFoundry() {
        Map<String, Object> cfProps = new HashMap<>();
        cfProps.put("VCAP_APPLICATION", "{\"application_id\":\"test-app\"}");
        cfProps.put("VCAP_SERVICES", "{\"mysql\":[{\"credentials\":{}}]}");

        applyPlatformProperties("cloud-foundry-simulation", cfProps);
        verifyPlatform(CloudPlatform.CLOUD_FOUNDRY);
    }

    public void simulateAws() {
        Map<String, Object> awsProps = new HashMap<>();
        awsProps.put("AWS_EXECUTION_ENV", "AWS_ECS_FARGATE");
        awsProps.put("AWS_REGION", "us-east-1");

        applyPlatformProperties("aws-simulation", awsProps);
        verifyPlatform(CloudPlatform.AMAZON_WEB_SERVICES);
    }

    public void simulateAzure() {
        Map<String, Object> azureProps = new HashMap<>();
        azureProps.put("WEBSITE_INSTANCE_ID", "test-instance-123");
        azureProps.put("WEBSITE_SITE_NAME", "test-app");

        applyPlatformProperties("azure-simulation", azureProps);
        verifyPlatform(CloudPlatform.AZURE);
    }

    public void simulateHeroku() {
        Map<String, Object> herokuProps = new HashMap<>();
        herokuProps.put("DYNO", "web.1");
        herokuProps.put("PORT", "5000");

        applyPlatformProperties("heroku-simulation", herokuProps);
        verifyPlatform(CloudPlatform.HEROKU);
    }

    public void simulateLocal() {
        // Remove all cloud platform environment variables
        removePlatformProperties();
        verifyPlatform(CloudPlatform.NONE);
    }

    private void applyPlatformProperties(String sourceName, Map<String, Object> props) {
        MapPropertySource propertySource = new MapPropertySource(sourceName, props);
        environment.getPropertySources().addFirst(propertySource);

        System.out.println("Applied platform simulation: " + sourceName);
        System.out.println("Properties: " + props);
    }

    private void removePlatformProperties() {
        environment.getPropertySources().remove("kubernetes-simulation");
        environment.getPropertySources().remove("cloud-foundry-simulation");
        environment.getPropertySources().remove("aws-simulation");
        environment.getPropertySources().remove("azure-simulation");
        environment.getPropertySources().remove("heroku-simulation");
    }

    private void verifyPlatform(CloudPlatform expected) {
        CloudPlatform actual = CloudPlatform.getActive(environment);

        if (actual != expected) {
            throw new IllegalStateException(
                String.format("Platform verification failed. Expected: %s, Actual: %s",
                    expected, actual)
            );
        }

        System.out.println("Successfully simulating platform: " + expected);
    }

    public void runCompatibilityTest() {
        System.out.println("\n=== Running Cloud Platform Compatibility Test ===\n");

        try {
            System.out.println("Testing Kubernetes compatibility...");
            simulateKubernetes();
            testPlatformBehavior();

            System.out.println("\nTesting Cloud Foundry compatibility...");
            simulateCloudFoundry();
            testPlatformBehavior();

            System.out.println("\nTesting AWS compatibility...");
            simulateAws();
            testPlatformBehavior();

            System.out.println("\nTesting Azure compatibility...");
            simulateAzure();
            testPlatformBehavior();

            System.out.println("\nTesting Heroku compatibility...");
            simulateHeroku();
            testPlatformBehavior();

            System.out.println("\n=== All compatibility tests passed ===\n");

        } catch (Exception e) {
            System.err.println("Compatibility test failed: " + e.getMessage());
            throw e;
        } finally {
            simulateLocal();
        }
    }

    private void testPlatformBehavior() {
        CloudPlatform platform = CloudPlatform.getActive(environment);
        System.out.println("  Current platform: " + platform);
        System.out.println("  Is cloud: " + CloudPlatform.isCloudPlatform(environment));
        System.out.println("  Platform active: " + platform.isActive(environment));
    }
}

Pattern 4: Platform-Specific Feature Flags

Complete feature flag system based on cloud platform:

package com.example.features;

import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Platform-aware feature flag system with override support.
 */
@Service
public class PlatformFeatureFlagService {

    private final Environment environment;
    private final CloudPlatform platform;
    private final Map<String, FeatureFlag> features;

    public PlatformFeatureFlagService(Environment environment) {
        this.environment = environment;
        this.platform = CloudPlatform.getActive(environment);
        this.features = new ConcurrentHashMap<>();

        initializeFeatures();
    }

    private void initializeFeatures() {
        // Define features with platform-specific defaults
        registerFeature("distributed-tracing",
            Set.of(CloudPlatform.KUBERNETES, CloudPlatform.AMAZON_WEB_SERVICES,
                   CloudPlatform.AZURE, CloudPlatform.CLOUD_FOUNDRY));

        registerFeature("auto-scaling",
            Set.of(CloudPlatform.KUBERNETES, CloudPlatform.AMAZON_WEB_SERVICES,
                   CloudPlatform.AZURE, CloudPlatform.CLOUD_FOUNDRY));

        registerFeature("service-mesh",
            Set.of(CloudPlatform.KUBERNETES));

        registerFeature("cloud-native-logging",
            Set.of(CloudPlatform.KUBERNETES, CloudPlatform.AMAZON_WEB_SERVICES,
                   CloudPlatform.AZURE, CloudPlatform.CLOUD_FOUNDRY));

        registerFeature("external-secrets",
            Set.of(CloudPlatform.KUBERNETES, CloudPlatform.AMAZON_WEB_SERVICES,
                   CloudPlatform.AZURE));

        registerFeature("health-probes",
            Set.of(CloudPlatform.KUBERNETES, CloudPlatform.AMAZON_WEB_SERVICES,
                   CloudPlatform.AZURE, CloudPlatform.CLOUD_FOUNDRY));

        registerFeature("container-optimization",
            Set.of(CloudPlatform.KUBERNETES, CloudPlatform.AMAZON_WEB_SERVICES,
                   CloudPlatform.AZURE, CloudPlatform.CLOUD_FOUNDRY, CloudPlatform.HEROKU));

        registerFeature("graceful-shutdown",
            Set.of(CloudPlatform.KUBERNETES, CloudPlatform.AMAZON_WEB_SERVICES,
                   CloudPlatform.AZURE, CloudPlatform.CLOUD_FOUNDRY));

        registerFeature("native-load-balancing",
            Set.of(CloudPlatform.KUBERNETES, CloudPlatform.AMAZON_WEB_SERVICES,
                   CloudPlatform.AZURE, CloudPlatform.CLOUD_FOUNDRY));

        registerFeature("serverless-optimization",
            Set.of(CloudPlatform.AMAZON_WEB_SERVICES)); // Lambda-specific
    }

    private void registerFeature(String featureName, Set<CloudPlatform> enabledPlatforms) {
        boolean defaultEnabled = enabledPlatforms.contains(platform);

        // Check for property override
        String propertyKey = "features." + featureName + ".enabled";
        Boolean override = environment.getProperty(propertyKey, Boolean.class);

        boolean enabled = (override != null) ? override : defaultEnabled;

        features.put(featureName, new FeatureFlag(
            featureName,
            enabled,
            enabledPlatforms,
            override != null
        ));

        System.out.printf("Feature '%s': %s (platform: %s, override: %s)%n",
            featureName, enabled ? "ENABLED" : "DISABLED", platform, override != null);
    }

    public boolean isEnabled(String featureName) {
        FeatureFlag feature = features.get(featureName);
        return feature != null && feature.enabled;
    }

    public boolean isEnabledForPlatform(String featureName, CloudPlatform targetPlatform) {
        FeatureFlag feature = features.get(featureName);
        return feature != null && feature.enabledPlatforms.contains(targetPlatform);
    }

    public Map<String, Boolean> getAllFeatures() {
        Map<String, Boolean> result = new HashMap<>();
        features.forEach((name, flag) -> result.put(name, flag.enabled));
        return result;
    }

    public void printFeatureReport() {
        System.out.println("\n=== Feature Flag Report ===");
        System.out.println("Platform: " + platform);
        System.out.println("\nFeature Status:");

        features.forEach((name, flag) -> {
            System.out.printf("  %-30s %s%s%n",
                name,
                flag.enabled ? "[ENABLED] " : "[DISABLED]",
                flag.overridden ? " (overridden)" : ""
            );
        });

        System.out.println();
    }

    private static class FeatureFlag {
        final String name;
        final boolean enabled;
        final Set<CloudPlatform> enabledPlatforms;
        final boolean overridden;

        FeatureFlag(String name, boolean enabled, Set<CloudPlatform> enabledPlatforms,
                   boolean overridden) {
            this.name = name;
            this.enabled = enabled;
            this.enabledPlatforms = enabledPlatforms;
            this.overridden = overridden;
        }
    }
}

Pattern 5: Platform-Specific Health Indicators

Comprehensive health check system with platform-specific checks:

package com.example.health;

import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.cloud.CloudPlatform;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/**
 * Platform-aware health indicator with platform-specific checks.
 */
@Component
public class PlatformHealthIndicator implements HealthIndicator {

    private final Environment environment;
    private final CloudPlatform platform;
    private final HttpClient httpClient;

    public PlatformHealthIndicator(Environment environment) {
        this.environment = environment;
        this.platform = CloudPlatform.getActive(environment);
        this.httpClient = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(5))
            .build();
    }

    @Override
    public Health health() {
        Map<String, Object> details = new HashMap<>();

        details.put("platform", platform.name());
        details.put("isCloud", CloudPlatform.isCloudPlatform(environment));

        // Platform-specific health checks
        boolean platformHealthy = switch (platform) {
            case KUBERNETES -> checkKubernetesHealth(details);
            case CLOUD_FOUNDRY -> checkCloudFoundryHealth(details);
            case AMAZON_WEB_SERVICES -> checkAwsHealth(details);
            case AZURE -> checkAzureHealth(details);
            case HEROKU -> checkHerokuHealth(details);
            default -> checkLocalHealth(details);
        };

        if (platformHealthy) {
            return Health.up()
                .withDetails(details)
                .build();
        } else {
            return Health.down()
                .withDetails(details)
                .build();
        }
    }

    private boolean checkKubernetesHealth(Map<String, Object> details) {
        boolean healthy = true;

        // Check if Kubernetes API is accessible
        String kubernetesHost = environment.getProperty("KUBERNETES_SERVICE_HOST");
        String kubernetesPort = environment.getProperty("KUBERNETES_SERVICE_PORT");

        details.put("kubernetes.api.host", kubernetesHost);
        details.put("kubernetes.api.port", kubernetesPort);

        // Check ConfigMaps and Secrets availability
        Path configPath = Path.of("/etc/config");
        Path secretPath = Path.of("/etc/secrets");

        if (Files.exists(configPath)) {
            details.put("configmap.mounted", true);
        } else {
            details.put("configmap.mounted", false);
        }

        if (Files.exists(secretPath)) {
            details.put("secrets.mounted", true);
        } else {
            details.put("secrets.mounted", false);
        }

        // Check pod metadata
        String podName = environment.getProperty("HOSTNAME");
        details.put("pod.name", podName);

        return healthy;
    }

    private boolean checkCloudFoundryHealth(Map<String, Object> details) {
        String vcapApplication = environment.getProperty("VCAP_APPLICATION");
        String vcapServices = environment.getProperty("VCAP_SERVICES");

        details.put("vcap.application.present", vcapApplication != null);
        details.put("vcap.services.present", vcapServices != null);

        if (vcapApplication == null && vcapServices == null) {
            details.put("warning", "VCAP environment variables not found");
            return false;
        }

        return true;
    }

    private boolean checkAwsHealth(Map<String, Object> details) {
        String region = environment.getProperty("AWS_REGION");
        String executionEnv = environment.getProperty("AWS_EXECUTION_ENV");

        details.put("aws.region", region);
        details.put("aws.execution.env", executionEnv);

        // Check if Lambda
        String lambdaFunction = environment.getProperty("AWS_LAMBDA_FUNCTION_NAME");
        if (lambdaFunction != null) {
            details.put("aws.lambda", true);
            details.put("aws.lambda.function", lambdaFunction);
        }

        // Check ECS metadata
        String ecsMetadataUri = environment.getProperty("ECS_CONTAINER_METADATA_URI_V4");
        if (ecsMetadataUri != null) {
            details.put("aws.ecs", true);
            details.put("aws.ecs.metadata.available", checkEcsMetadata(ecsMetadataUri));
        }

        return true;
    }

    private boolean checkAzureHealth(Map<String, Object> details) {
        String instanceId = environment.getProperty("WEBSITE_INSTANCE_ID");
        String siteName = environment.getProperty("WEBSITE_SITE_NAME");

        details.put("azure.instance.id", instanceId);
        details.put("azure.site.name", siteName);

        if (instanceId == null || siteName == null) {
            details.put("warning", "Azure environment variables not found");
            return false;
        }

        return true;
    }

    private boolean checkHerokuHealth(Map<String, Object> details) {
        String dyno = environment.getProperty("DYNO");
        String port = environment.getProperty("PORT");

        details.put("heroku.dyno", dyno);
        details.put("heroku.port", port);

        if (dyno == null) {
            details.put("warning", "DYNO environment variable not found");
            return false;
        }

        return true;
    }

    private boolean checkLocalHealth(Map<String, Object> details) {
        details.put("environment", "local");
        return true;
    }

    private boolean checkEcsMetadata(String metadataUri) {
        try {
            HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(metadataUri))
                .timeout(Duration.ofSeconds(3))
                .GET()
                .build();

            HttpResponse<String> response = httpClient.send(request,
                HttpResponse.BodyHandlers.ofString());

            return response.statusCode() == 200;
        } catch (IOException | InterruptedException e) {
            return false;
        }
    }
}

Common Pitfalls

1. Forced Platform Not Applied

Problem: Setting spring.main.cloud-platform doesn't take effect

Error:

Application detects wrong platform despite configuration

Solution:

# Correct property name (with dashes, not dots in the value)
spring.main.cloud-platform=kubernetes

# NOT this (underscores in value won't work):
spring.main.cloud-platform=KUBERNETES
// Programmatic forcing during tests
@TestPropertySource(properties = {
    "spring.main.cloud-platform=kubernetes"
})
class MyTest {
    @Test
    void testKubernetesBehavior() {
        CloudPlatform platform = CloudPlatform.getActive(environment);
        assertThat(platform).isEqualTo(CloudPlatform.KUBERNETES);
    }
}

Rationale: The property value must be lowercase with dashes, matching the enum name conversion logic. Spring Boot normalizes the value by converting to uppercase and replacing dashes with underscores before calling valueOf().

2. Detection Order Confusion

Problem: Multiple platform environment variables present causes wrong detection

Error: Application detects unexpected platform when running in Docker on Kubernetes

Solution:

// Explicitly force the correct platform
@Configuration
public class PlatformConfig {

    @Bean
    public ApplicationRunner platformChecker(Environment environment) {
        return args -> {
            CloudPlatform detected = CloudPlatform.getActive(environment);
            CloudPlatform expected = CloudPlatform.KUBERNETES;

            if (detected != expected) {
                throw new IllegalStateException(
                    String.format("Wrong platform detected. Expected: %s, Got: %s. " +
                        "Set spring.main.cloud-platform=%s",
                        expected, detected, expected.name().toLowerCase().replace('_', '-'))
                );
            }
        };
    }
}
# Force specific platform to avoid detection issues
spring.main.cloud-platform=kubernetes

Rationale: CloudPlatform checks platforms in order defined in the enum. If multiple platform environment variables exist (e.g., running Docker locally with AWS credentials), the first matching platform is detected. Always explicitly set the platform property in ambiguous environments.

3. Missing Environment Variables in Tests

Problem: Platform detection fails in tests due to missing environment variables

Error:

Platform is NONE in tests, expected KUBERNETES

Solution:

@SpringBootTest
@TestPropertySource(properties = {
    "KUBERNETES_SERVICE_HOST=10.0.0.1",
    "KUBERNETES_SERVICE_PORT=443"
})
class KubernetesPlatformTest {

    @Autowired
    private Environment environment;

    @Test
    void shouldDetectKubernetes() {
        CloudPlatform platform = CloudPlatform.getActive(environment);
        assertThat(platform).isEqualTo(CloudPlatform.KUBERNETES);
    }
}

// Or use system properties
@SpringBootTest
class CloudFoundryTest {

    @BeforeEach
    void setupPlatform() {
        System.setProperty("VCAP_APPLICATION", "{\"application_id\":\"test\"}");
    }

    @AfterEach
    void tearDown() {
        System.clearProperty("VCAP_APPLICATION");
    }
}

Rationale: Tests run without cloud environment variables. You must either inject them via @TestPropertySource or use spring.main.cloud-platform to force platform detection for testing.

4. Platform-Specific Code Without Fallback

Problem: Application crashes when platform detection returns NONE

Error:

NullPointerException when accessing platform-specific configuration

Solution:

@Service
public class ConfigService {

    private final Environment environment;

    public String getServiceUrl(String serviceName) {
        CloudPlatform platform = CloudPlatform.getActive(environment);

        // Always provide fallback for NONE
        return switch (platform) {
            case KUBERNETES -> getKubernetesServiceUrl(serviceName);
            case CLOUD_FOUNDRY -> getCloudFoundryServiceUrl(serviceName);
            case AMAZON_WEB_SERVICES -> getAwsServiceUrl(serviceName);
            case AZURE -> getAzureServiceUrl(serviceName);
            case HEROKU -> getHerokuServiceUrl(serviceName);
            case SAP -> getSapServiceUrl(serviceName);
            case NONE -> getLocalServiceUrl(serviceName); // Always provide fallback
        };
    }

    private String getLocalServiceUrl(String serviceName) {
        // Default for local development
        return environment.getProperty(
            serviceName + ".url",
            "http://localhost:8080" // Safe default
        );
    }
}

Rationale: CloudPlatform.NONE is the default when no platform is detected. Code that switches on platform must always handle NONE to avoid crashes in local development or unexpected environments.

5. Conditional Beans Without Platform Check

Problem: @ConditionalOnCloudPlatform beans fail to register

Error:

NoSuchBeanDefinitionException: No qualifying bean of type 'ServiceDiscovery'

Solution:

@Configuration
public class ServiceDiscoveryConfig {

    // Platform-specific beans
    @Bean
    @ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
    public ServiceDiscovery kubernetesServiceDiscovery() {
        return new KubernetesServiceDiscovery();
    }

    @Bean
    @ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
    public ServiceDiscovery cloudFoundryServiceDiscovery() {
        return new CloudFoundryServiceDiscovery();
    }

    // IMPORTANT: Always provide a fallback bean for non-cloud environments
    @Bean
    @ConditionalOnCloudPlatform(CloudPlatform.NONE)
    public ServiceDiscovery localServiceDiscovery() {
        return new LocalServiceDiscovery();
    }

    // OR use @ConditionalOnMissingBean as ultimate fallback
    @Bean
    @ConditionalOnMissingBean
    public ServiceDiscovery defaultServiceDiscovery() {
        return new LocalServiceDiscovery();
    }
}

Rationale: When using @ConditionalOnCloudPlatform, you must provide a bean for every possible platform, or use @ConditionalOnMissingBean as a fallback. Without a fallback, the application fails to start when no matching platform is detected.

6. Platform Detection Too Early

Problem: Checking platform before Environment is fully initialized

Error:

Platform detection returns NONE during EnvironmentPostProcessor

Solution:

// WRONG: Checking platform too early
@Component
public class EarlyPlatformCheck implements EnvironmentPostProcessor {

    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment,
                                      SpringApplication application) {
        // Platform environment variables might not be loaded yet
        CloudPlatform platform = CloudPlatform.getActive(environment); // May be NONE
    }
}

// CORRECT: Check platform after context is initialized
@Component
public class PlatformCheck implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {

    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
        // Environment is fully prepared with all sources
        CloudPlatform platform = CloudPlatform.getActive(event.getEnvironment());
        System.out.println("Detected platform: " + platform);
    }
}

// OR use ApplicationReadyEvent for guaranteed detection
@Component
public class LatePlatformCheck {

    @EventListener(ApplicationReadyEvent.class)
    public void onReady(ApplicationReadyEvent event) {
        CloudPlatform platform = CloudPlatform.getActive(event.getApplicationContext().getEnvironment());
        // Platform is definitely detected by this point
    }
}

Rationale: Platform detection depends on environment variables which are loaded at specific lifecycle points. Checking too early (e.g., in constructors, EnvironmentPostProcessor) may return NONE even in cloud environments. Wait for ApplicationEnvironmentPreparedEvent or later events.

7. Kubernetes Detection in Docker

Problem: Application thinks it's running on Kubernetes when it's just Docker

Error: Application enables Kubernetes-specific features in non-Kubernetes Docker containers

Solution:

# In Dockerfile, do NOT set these unless actually running on Kubernetes:
# KUBERNETES_SERVICE_HOST
# KUBERNETES_SERVICE_PORT

# These are automatically set by Kubernetes, don't set them manually
# If needed, force non-Kubernetes platform
spring.main.cloud-platform=none
// Defensive programming - verify Kubernetes features
@Component
@ConditionalOnCloudPlatform(CloudPlatform.KUBERNETES)
public class KubernetesFeatures {

    @PostConstruct
    public void verifyKubernetesEnvironment() {
        // Verify actual Kubernetes features are available
        Path serviceAccountPath = Path.of("/var/run/secrets/kubernetes.io/serviceaccount");

        if (!Files.exists(serviceAccountPath)) {
            throw new IllegalStateException(
                "Kubernetes platform detected but service account not found. " +
                "This may be a false detection. Set spring.main.cloud-platform=none"
            );
        }
    }
}

Rationale: Setting Kubernetes environment variables manually in Docker causes false platform detection. These variables should only be present when actually running in Kubernetes. Always let Kubernetes inject these variables automatically.

8. Cloud Foundry VCAP Parsing Errors

Problem: Incorrect parsing of VCAP_SERVICES JSON

Error:

JsonParseException when accessing Cloud Foundry services

Solution:

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

@Component
@ConditionalOnCloudPlatform(CloudPlatform.CLOUD_FOUNDRY)
public class VcapServicesParser {

    private final ObjectMapper objectMapper = new ObjectMapper();

    public Map<String, Object> parseVcapServices(String vcapJson) {
        if (vcapJson == null || vcapJson.isEmpty()) {
            return Collections.emptyMap();
        }

        try {
            return objectMapper.readValue(vcapJson,
                new TypeReference<Map<String, Object>>() {});
        } catch (JsonProcessingException e) {
            throw new IllegalStateException(
                "Failed to parse VCAP_SERVICES JSON: " + e.getMessage(), e
            );
        }
    }

    public String getServiceCredential(String serviceType, String serviceName,
                                      String credentialKey) {
        String vcapServices = System.getenv("VCAP_SERVICES");
        Map<String, Object> services = parseVcapServices(vcapServices);

        try {
            List<Map<String, Object>> serviceList =
                (List<Map<String, Object>>) services.get(serviceType);

            if (serviceList == null) {
                throw new IllegalArgumentException(
                    "Service type not found in VCAP_SERVICES: " + serviceType
                );
            }

            for (Map<String, Object> service : serviceList) {
                if (serviceName.equals(service.get("name"))) {
                    Map<String, Object> credentials =
                        (Map<String, Object>) service.get("credentials");
                    return (String) credentials.get(credentialKey);
                }
            }

            throw new IllegalArgumentException(
                "Service not found: " + serviceName
            );

        } catch (ClassCastException e) {
            throw new IllegalStateException(
                "Unexpected VCAP_SERVICES structure: " + e.getMessage(), e
            );
        }
    }
}

Rationale: VCAP_SERVICES is a complex nested JSON structure. Use a proper JSON parser (Jackson, Gson) and handle parsing errors gracefully. Never use string manipulation or regex to parse VCAP JSON.

9. AWS Lambda vs ECS Detection

Problem: Cannot distinguish between Lambda and ECS in AWS

Error: Application applies Lambda optimizations to ECS containers

Solution:

@Service
public class AwsPlatformDetector {

    private final Environment environment;

    public boolean isAwsLambda() {
        return environment.getProperty("AWS_LAMBDA_FUNCTION_NAME") != null;
    }

    public boolean isAwsEcs() {
        return environment.getProperty("AWS_EXECUTION_ENV") != null &&
               environment.getProperty("AWS_LAMBDA_FUNCTION_NAME") == null;
    }

    public boolean isAwsEc2() {
        // EC2 instances have AWS_EXECUTION_ENV but no Lambda or ECS specific vars
        return environment.getProperty("AWS_EXECUTION_ENV") != null &&
               environment.getProperty("AWS_LAMBDA_FUNCTION_NAME") == null &&
               environment.getProperty("ECS_CONTAINER_METADATA_URI") == null;
    }

    @PostConstruct
    public void configureForAwsEnvironment() {
        if (!CloudPlatform.AMAZON_WEB_SERVICES.isActive(environment)) {
            return;
        }

        if (isAwsLambda()) {
            System.out.println("Configuring for AWS Lambda");
            configureLambda();
        } else if (isAwsEcs()) {
            System.out.println("Configuring for AWS ECS");
            configureEcs();
        } else {
            System.out.println("Configuring for AWS EC2");
            configureEc2();
        }
    }

    private void configureLambda() {
        // Lambda-specific: disable web server, optimize cold starts
    }

    private void configureEcs() {
        // ECS-specific: enable container insights
    }

    private void configureEc2() {
        // EC2-specific: standard configuration
    }
}

Rationale: CloudPlatform.AMAZON_WEB_SERVICES matches multiple AWS environments (Lambda, ECS, EC2). You must check specific environment variables to distinguish between them and apply appropriate optimizations.

10. Heroku Port Binding

Problem: Application doesn't bind to Heroku-assigned PORT

Error:

Error R10 (Boot timeout) -> Web process failed to bind to $PORT within 60 seconds

Solution:

# application.properties
# Use PORT environment variable on Heroku, fallback to 8080
server.port=${PORT:8080}
// Or programmatically
@Configuration
@ConditionalOnCloudPlatform(CloudPlatform.HEROKU)
public class HerokuConfig {

    @Bean
    public WebServerFactoryCustomizer<ConfigurableWebServerFactory> herokuPortCustomizer(
            Environment environment) {
        return factory -> {
            String port = environment.getProperty("PORT");
            if (port != null) {
                factory.setPort(Integer.parseInt(port));
                System.out.println("Heroku port configured: " + port);
            }
        };
    }
}
# application.yml
server:
  port: ${PORT:8080}

Rationale: Heroku dynamically assigns the PORT environment variable. Applications must bind to this port, not a hardcoded port. Using ${PORT:8080} syntax provides Heroku compatibility while maintaining local development defaults.

Thread Safety

CloudPlatform: Enum is inherently thread-safe. Detection methods are stateless and can be called concurrently.

Best Practices

  1. Use Conditional Configuration: Leverage @ConditionalOnCloudPlatform for platform-specific beans
  2. Feature Flags: Toggle features based on platform capabilities
  3. Testing: Use spring.main.cloud-platform to test platform-specific code
  4. Defaults: Always provide sensible defaults for NONE platform
  5. Environment Variables: Respect platform-specific environment variables
  6. Health Checks: Include platform info in health endpoints

Import Statements

// Cloud platform detection
import org.springframework.boot.cloud.CloudPlatform;

// Conditional configuration
import org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform;

// Environment access
import org.springframework.core.env.Environment;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;