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
Related Errors
- **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