Introduction

Spring Boot application startup failures occur when the Spring ApplicationContext cannot initialize due to bean creation errors, auto-configuration conflicts, embedded server issues, or external dependency failures, preventing the application from accepting requests. Spring Boot's auto-configuration mechanism scans the classpath, detects available dependencies, and automatically configures beans accordingly. However, this convention-over-configuration approach can fail when: required properties are missing, bean dependencies form circular references, database connections cannot be established, embedded server (Tomcat/Jetty/Undertow) fails to bind to port, required auto-configuration classes conflict, classpath contains incompatible library versions, or environment variables override expected defaults. Common causes include @Autowired injection failures, @Bean methods throwing exceptions during initialization, database connection pool exhaustion during startup, Hibernate schema validation failing, port already in use (address already bound), SSL/TLS certificate loading failures, actuator health check dependencies unavailable, cloud configuration server unreachable, and missing @Enable* annotations for required features. The fix requires understanding Spring Boot's startup lifecycle, auto-configuration ordering, bean initialization phases, embedded server configuration, and debugging tools for diagnosing startup failures. This guide provides production-proven troubleshooting patterns for Spring Boot applications across bare metal, Docker, Kubernetes, and cloud platforms (AWS, Azure, GCP).

Symptoms

  • Application exits immediately after starting with exit code 1
  • Console shows "APPLICATION_FAILED_TO_START" error
  • Stack trace shows "Error creating bean with name"
  • Embedded Tomcat fails with "Port already in use"
  • Database connection timeout during startup
  • "Circular reference containing beans" error
  • Auto-configuration report shows negative matches
  • Actuator health endpoint returns DOWN status
  • Kubernetes pod enters CrashLoopBackOff
  • Cloud Foundry app fails to start (Crash)
  • SSL certificate not found or invalid
  • Configuration property binding fails
  • Hibernate schema validation errors
  • Message queue connection refused

Common Causes

  • Bean creation failure (@Bean method throws exception)
  • Circular dependency between beans
  • Missing required configuration properties
  • Database unreachable or credentials wrong
  • Port conflict (another process bound to port)
  • Auto-configuration class conflicts
  • Incompatible library versions on classpath
  • Embedded server misconfiguration
  • SSL/TLS certificate loading failure
  • Cloud service binding failures

Step-by-Step Fix

### 1. Diagnose startup failures

Enable debug logging for startup:

```bash # Run with debug flag java -jar myapp.jar --debug

# Or set environment variable export DEBUG=true java -jar myapp.jar

# Or in application.properties debug=true trace=true

# Debug output shows: # - CONDITION EVALUATION REPORT # - Which auto-configurations matched # - Which auto-configurations didn't match (and why) # - Bean creation order # - Property binding details ```

Analyze Condition Evaluation Report:

``` # In debug output, look for:

============================ CONDITIONS EVALUATION REPORT ============================

Positive matches: -----------------

DataSourceAutoConfiguration matched: - @ConditionalOnClass found required classes 'javax.sql.DataSource', 'org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType' (OnClassCondition) - @ConditionalOnMissingBean (types: org.springframework.boot.autoconfigure.jdbc.DataSourceSchemaCreatedEvent; SearchStrategy: all) found no primary beans from bean factory (OnBeanCondition)

Negative matches: -----------------

RedisAutoConfiguration did not match: - @ConditionalOnClass did not find required class 'org.springframework.data.redis.core.RedisOperations' (OnClassCondition) - No redis.properties found (OnPropertyCondition)

# If expected auto-configuration shows in "Negative matches": # - Check if required dependencies are on classpath # - Check if required properties are set ```

Check bean creation errors:

```bash # Typical bean creation error:

*************************** APPLICATION FAILED TO START ***************************

Description:

Parameter 0 of constructor in com.example.MyService required a bean of type 'com.example.Repository' that could not be found.

Action:

Consider defining a bean of type 'com.example.Repository' in your configuration.

# This means: # - MyService has @Autowired Repository # - Spring cannot find a Repository bean # - Either Repository is not annotated with @Repository # - Or Repository's own dependencies failed ```

Enable startup logging:

```java // In application.properties logging.level.org.springframework.boot=DEBUG logging.level.org.springframework.context=DEBUG logging.level.org.springframework.beans=DEBUG

// Or via command line java -jar myapp.jar \ --logging.level.org.springframework.boot=DEBUG \ --logging.level.org.springframework.context=DEBUG ```

### 2. Fix circular dependency errors

Identify circular references:

```java // Circular dependency example: @Service public class ServiceA { @Autowired private ServiceB serviceB; // A depends on B }

@Service public class ServiceB { @Autowired private ServiceA serviceA; // B depends on A = circular! }

// Spring throws: // BeanCurrentlyInCreationException: // Error creating bean with name 'serviceA': // Requested bean is currently in creation: has this bean been injected into itself? ```

Fix with @Lazy annotation:

```java // Option 1: Use @Lazy on one side @Service public class ServiceA { @Autowired @Lazy // Inject proxy, not actual bean private ServiceB serviceB; }

@Service public class ServiceB { @Autowired private ServiceA serviceA; // No @Lazy needed here }

// @Lazy creates a proxy that resolves the actual bean when first used // Breaks the circular initialization chain ```

Fix with setter injection:

```java // Option 2: Use setter injection for one dependency @Service public class ServiceA { private ServiceB serviceB;

@Autowired // Setter injection (not constructor) public void setServiceB(ServiceB serviceB) { this.serviceB = serviceB; } }

@Service public class ServiceB { @Autowired private ServiceA serviceA; }

// Setter injection happens after constructor injection // Allows both beans to be created first ```

Fix with @PostConstruct:

```java // Option 3: Access dependency after initialization @Service public class ServiceA { @Autowired private ServiceB serviceB;

private SomeData data;

@PostConstruct public void init() { // Access serviceB here, after both beans created data = serviceB.loadData(); } }

// Constructor runs during bean creation // @PostConstruct runs after all beans created ```

Refactor to avoid circular dependency:

```java // Best option: Extract shared logic to third service @Service public class SharedService { public void sharedMethod() { } }

@Service public class ServiceA { @Autowired private SharedService sharedService; // A depends on Shared }

@Service public class ServiceB { @Autowired private SharedService sharedService; // B depends on Shared // No circular dependency! } ```

### 3. Fix database connection failures

Configure database connection properly:

```properties # application.properties

# MySQL/MariaDB spring.datasource.url=jdbc:mysql://localhost:3306/mydb spring.datasource.username=myuser spring.datasource.password=mypassword spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

# PostgreSQL spring.datasource.url=jdbc:postgresql://localhost:5432/mydb spring.datasource.username=myuser spring.datasource.password=mypassword spring.datasource.driver-class-name=org.postgresql.Driver

# HikariCP connection pool (default) spring.datasource.hikari.maximum-pool-size=10 spring.datasource.hikari.minimum-idle=5 spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.idle-timeout=600000 spring.datasource.hikari.max-lifetime=1800000 ```

Add retry logic for database startup:

```java // Retry database connection during startup @Configuration public class DatabaseConfig {

@Bean @Retryable( value = {SQLException.class}, maxAttempts = 5, backoff = @Backoff(delay = 2000) ) public DataSource dataSource() { HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://localhost:3306/mydb"); config.setUsername("myuser"); config.setPassword("mypassword"); return new HikariDataSource(config); }

@Bean public RetryTemplate retryTemplate() { return RetryTemplate.builder() .maxAttempts(5) .exponentialBackoff(1000, 2, 10000) .retryOn(SQLException.class) .build(); } }

// Enable retry @EnableRetry @SpringBootApplication public class MyApplication { } ```

Configure Hibernate schema validation:

```properties # application.properties

# Validate schema on startup (fails if tables don't match entities) spring.jpa.hibernate.ddl-auto=validate

# Update schema automatically (dev only!) spring.jpa.hibernate.ddl-auto=update

# Create schema on startup (destroys data!) spring.jpa.hibernate.ddl-auto=create-drop

# No schema management (manual DDL) spring.jpa.hibernate.ddl-auto=none

# Show SQL for debugging spring.jpa.show-sql=true spring.jpa.properties.hibernate.format_sql=true ```

Disable database auto-configuration if not needed:

java // If your app doesn't need a database: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class }) public class MyApplication { }

### 4. Fix port conflict errors

Configure embedded server port:

```properties # application.properties

# Use random available port (good for testing) server.port=0

# Use specific port server.port=8080

# Disable HTTP entirely (for workers/batch jobs) server.port=-1

# Use environment variable server.port=${PORT:8080} ```

Handle port already in use:

```java // Custom error handler for port conflicts @Component public class ServerErrorHandler implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {

@Override public void customize(TomcatServletWebServerFactory factory) { factory.addConnectorCustomizers(connector -> { connector.setProperty("address", "0.0.0.0"); }); } }

// Or retry with different port @Bean public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> portCustomizer() { return factory -> { int port = 8080; while (!isPortAvailable(port)) { port++; // Try next port } factory.setPort(port); }; }

private boolean isPortAvailable(int port) { try (ServerSocket ss = new ServerSocket(port)) { return true; } catch (IOException e) { return false; } } ```

Configure different server type:

```properties # Switch from Tomcat to Jetty # pom.xml (Maven) <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency>

# Or switch to Undertow (lower memory footprint) <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-undertow</artifactId> </dependency> ```

### 5. Fix auto-configuration conflicts

Exclude specific auto-configurations:

```java // Exclude problematic auto-configurations @SpringBootApplication(exclude = { SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class, RedisAutoConfiguration.class, KafkaAutoConfiguration.class }) public class MyApplication { }

// Or in application.properties spring.autoconfigure.exclude=\ org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration ```

Control auto-configuration order:

```java // Use @AutoConfigureBefore or @AutoConfigureAfter @Configuration @AutoConfigureBefore(DataSourceAutoConfiguration.class) public class CustomDataSourceConfig {

@Bean @Primary // Override default DataSource public DataSource dataSource() { // Custom DataSource configuration } }

@Configuration @AutoConfigureAfter(RedisAutoConfiguration.class) public class CustomRedisConfig {

@Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { // Custom RedisTemplate } } ```

Debug auto-configuration:

```bash # Generate condition evaluation report java -jar myapp.jar --debug > startup-log.txt

# Search for specific auto-configuration grep -A 10 "MyAutoConfiguration" startup-log.txt

# Check which properties triggered auto-config grep "OnPropertyCondition" startup-log.txt ```

### 6. Fix SSL/TLS certificate errors

Configure SSL properly:

```properties # application.properties

# Enable HTTPS server.port=8443 server.ssl.enabled=true

# Keystore configuration server.ssl.key-store=classpath:keystore.p12 server.ssl.key-store-password=changeit server.ssl.key-store-type=PKCS12 server.ssl.key-alias=myapp

# Or JKS format server.ssl.key-store=classpath:keystore.jks server.ssl.key-store-password=changeit server.ssl.key-store-type=JKS ```

Generate development certificate:

```bash # Generate self-signed certificate for development keytool -genkeypair \ -alias myapp \ -keyalg RSA \ -keysize 2048 \ -storetype PKCS12 \ -keystore keystore.p12 \ -validity 365 \ -storepass changeit \ -keypass changeit \ -dname "CN=localhost,OU=Development,O=MyCompany,L=City,ST=State,C=US"

# Place keystore.p12 in src/main/resources/ ```

Configure SSL in code:

java @Bean public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> sslCustomizer() { return factory -> { Ssl ssl = new Ssl(); ssl.setEnabled(true); ssl.setKeyStore("classpath:keystore.p12"); ssl.setKeyStorePassword("changeit"); ssl.setKeyStoreType("PKCS12"); factory.setSsl(ssl); }; }

### 7. Fix configuration property binding errors

Validate required properties:

```java // Use @ConfigurationProperties with validation @Component @ConfigurationProperties(prefix = "app.database") @Validated public class DatabaseProperties {

@NotNull @NotBlank private String host;

@NotNull @Min(1) @Max(65535) private Integer port;

@NotNull @NotBlank private String username;

@NotNull private String password;

// Getters and setters

public String getUrl() { return "jdbc:mysql://" + host + ":" + port + "/mydb"; } }

// Enable validation @ConfigurationPropertiesScan @SpringBootApplication public class MyApplication { } ```

Use relaxed binding:

```properties # All of these bind to the same property: # myapp.maxPoolSize (camelCase) # myapp.max-pool-size (kebab-case) # myapp.max_pool_size (snake_case) # MYAPP_MAX_POOL_SIZE (environment variable)

# application.properties myapp.max-pool-size=10

# Or environment variable export MYAPP_MAX_POOL_SIZE=10

# Or command line java -jar myapp.jar --myapp.max-pool-size=10 ```

Handle missing optional properties:

```java @ConfigurationProperties(prefix = "app.feature") public class FeatureProperties {

// Default value if not set private boolean enabled = true;

// Default value private int batchSize = 100;

// Nullable optional property private String optionalConfig;

// Getters and setters } ```

### 8. Fix actuator health check failures

Configure health indicators:

```properties # Enable all health indicators management.health.defaults.enabled=true

# Enable specific indicators management.health.db.enabled=true management.health.redis.enabled=true management.health.rabbit.enabled=true management.health.elasticsearch.enabled=true

# Disable specific indicators management.health.mail.enabled=false management.health.mongo.enabled=false ```

Create custom health indicator:

```java @Component public class CustomHealthIndicator implements HealthIndicator {

@Override public Health health() { try { // Check custom dependency checkExternalService(); return Health.up() .withDetail("service", "external-api") .withDetail("responseTime", "50ms") .build(); } catch (Exception e) { return Health.down(e) .withDetail("service", "external-api") .withDetail("error", e.getMessage()) .build(); } }

private void checkExternalService() { // Custom health check logic } } ```

Configure health check timeout:

```properties # Health check timeout management.health.db.timeout=5s management.health.redis.timeout=3s

# Endpoint exposure management.endpoints.web.exposure.include=health,info,metrics management.endpoints.web.exposure.exclude=env,threaddump

# Show full health details (for debugging) management.endpoint.health.show-details=always management.endpoint.health.probes.enabled=true ```

### 9. Fix cloud configuration failures

Configure retry for config server:

```java // Add spring-retry dependency // pom.xml <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>

// Enable retry @EnableRetry @SpringBootApplication public class MyApplication { }

// Config client with retry // bootstrap.properties spring.cloud.config.uri=http://config-server:8888 spring.cloud.config.fail-fast=true spring.cloud.config.retry.initial-interval=1000 spring.cloud.config.retry.max-interval=5000 spring.cloud.config.retry.max-attempts=10 ```

Handle missing config server gracefully:

```properties # Use local fallback if config server unavailable # application.properties spring.config.import=optional:configserver:http://config-server:8888

# Or use local configuration as fallback spring.profiles.active=local,cloud spring.cloud.config.allow-override=true spring.cloud.config.override-none=true ```

### 10. Debug startup performance

Profile startup time:

```java // Enable startup logging logging.level.org.springframework.boot.autoconfigure=DEBUG

// Log bean creation time @Bean public static BeanPostProcessor timingPostProcessor() { return new BeanPostProcessor() { private long startTime;

@Override public Object postProcessBeforeInitialization(Object bean, String beanName) { startTime = System.currentTimeMillis(); return bean; }

@Override public Object postProcessAfterInitialization(Object bean, String beanName) { long elapsed = System.currentTimeMillis() - startTime; if (elapsed > 100) { // Log beans taking > 100ms System.out.println("Bean " + beanName + " took " + elapsed + "ms"); } return bean; } }; } ```

Use Spring Boot Startup Reporter:

```properties # Enable startup step recording management.startup.steps.record=true

# View startup steps via actuator curl http://localhost:8080/actuator/startup

# Or export to tracing system management.tracing.enabled=true management.tracing.sampling.probability=1.0 ```

Lazy initialize beans:

```properties # Enable lazy initialization (beans created on first use) spring.main.lazy-initialization=true

# Or lazy initialize specific beans @Bean @Lazy public ExpensiveBean expensiveBean() { return new ExpensiveBean(); } ```

Prevention

  • Use @SpringBootTest for integration tests that verify startup
  • Configure health checks for all external dependencies
  • Set reasonable timeouts for database and service connections
  • Use @ConfigurationProperties with validation for type safety
  • Enable debug logging in staging environments
  • Profile startup time periodically to catch regressions
  • Document required environment variables and properties
  • Use Docker health checks to verify application readiness
  • **BeanCreationException**: Bean initialization failed
  • **UnsatisfiedDependencyException**: @Autowired dependency not found
  • **PortInUseException**: Server port already bound
  • **CannotLoadBeanClassException**: Bean class not found on classpath
  • **IllegalStateException**: ApplicationContext closed during startup