Spring Boot ddl-auto=create-drop: Why It Destroys Your Production Data
Learn why Spring Boot's ddl-auto=create-drop setting can wipe out your production database.
20+ years shipping production Java in banking & fintech. Drawn from code that ran under real load.
- ✓Spring Boot 3.x or 2.x project with JPA and Hibernate
- ✓Basic understanding of application.properties or application.yml
- ✓Access to a database (PostgreSQL, MySQL, or H2 for testing)
• ddl-auto=create-drop drops all tables on application shutdown, causing irreversible data loss in production. • In Spring Boot 2.x and 3.x, this setting is often mistakenly left from development, leading to catastrophic incidents. • The correct approach: use 'validate' or 'none' in production, manage schema via Flyway or Liquibase. • Always externalize the setting via application properties and environment-specific profiles.
Think of ddl-auto=create-drop like a self-destruct button on a building. In development, you might press it daily to rebuild from scratch. But if you leave it active in production, the building (your database) will be demolished every time you restart the application. You wouldn't want that in a real office building, and you don't want it in your production database.
I've seen it happen more times than I care to count. A junior developer copies application.properties from a tutorial, fires up Spring Boot 3.2 in production, and within seconds, months of customer data vanish. The culprit? A single line: spring.jpa.hibernate.ddl-auto=create-drop. This setting, intended for rapid prototyping and testing, tells Hibernate to drop all tables when the application stops. In production, that means every deployment, every rollback, every unexpected shutdown becomes a data apocalypse. Spring Boot's auto-configuration (since version 1.0) makes schema generation dangerously easy. Combined with the 'create-drop' value, it's a recipe for disaster. The default behavior in Spring Boot is actually 'create-drop' for embedded databases (like H2) and 'none' for others, but many developers override this without understanding the consequences. I've debugged incidents where a payment processing system lost transaction records because a CI/CD pipeline triggered a graceful shutdown, and Hibernate obediently dropped every table. The fix? Never use 'create-drop' in production. Use Flyway (6.x+) or Liquibase (4.x+) for schema management. This article will walk you through the exact mechanism, real-world incidents, and how to protect your data.
How ddl-auto Works Under the Hood
When Spring Boot starts, it initializes the Hibernate SessionFactory. If spring.jpa.hibernate.ddl-auto is set (e.g., 'create-drop'), Hibernate's SchemaExport class executes DDL statements. For 'create-drop', it generates DROP TABLE statements for all entities on shutdown via a JPA shutdown hook. On startup, it runs CREATE TABLE statements. In Spring Boot 2.7+, the behavior is controlled by Hibernate's 'hibernate.hbm2ddl.auto' property. The 'create-drop' value is particularly dangerous because the drop happens in the Hibernate SessionFactory's close() method, which is called during ApplicationContext.close(). This can be triggered by: a graceful shutdown (POST /actuator/shutdown), a failed health check that restarts the context, or even a misconfigured CI/CD pipeline that sends SIGTERM. Let's see a minimal example that demonstrates this behavior.
What the Official Docs Won't Tell You
The official Spring Boot documentation (up to version 3.2) states that 'create-drop' is useful for testing, but it doesn't emphasize the production risks. It doesn't mention that the 'drop' happens on ApplicationContext.close(), which can be triggered by many things beyond a simple stop. For example, Spring Boot Actuator's restart endpoint (POST /actuator/restart) calls close() then createApplicationContext(). If ddl-auto=create-drop is set, your data is gone. Also, the docs don't cover that Hibernate's SchemaExport does not use transactions for DROP statements in some dialects (like PostgreSQL 14+), meaning the drop is immediate and cannot be rolled back. Another hidden detail: if you use Spring Cloud Config and refresh the context (POST /actuator/refresh), the old context closes and a new one starts. That's two drops in quick succession. The official docs also fail to mention that the default value for embedded databases (H2, HSQL) is 'create-drop', so if you switch from H2 to PostgreSQL without changing the property, you'll get the same behavior. Let's see a code snippet that shows how to detect this in a startup check.
Spring Profiles to the Rescue
The safest way to manage ddl-auto across environments is using Spring Profiles. Create separate application properties files for each environment. For development, you might use 'create-drop' for quick testing. For staging and production, use 'validate' or 'none'. Spring Boot loads application-{profile}.properties after the main application.properties, so you can override the setting. However, a common mistake is forgetting to set the active profile, which defaults to 'default'. If you only set ddl-auto in application-dev.properties and not in application.properties, running without a profile will use the default (which might be 'create-drop' for embedded databases). Always set a safe default in application.properties. Here's how to configure it properly.
Migration to Flyway: The Production-Grade Solution
Instead of relying on Hibernate's schema generation, use Flyway or Liquibase for database migrations. Flyway (version 9.x+) integrates seamlessly with Spring Boot. You set ddl-auto to 'none' or 'validate' and let Flyway manage schema changes. This gives you versioned, repeatable migrations that can be rolled back. The key advantage: no data loss on restart. Flyway only applies new migrations, never drops tables unless you explicitly write a migration for it. Spring Boot auto-configures Flyway if it finds the dependency on the classpath. Here's how to set it up.
Testing with create-drop Safely
There are valid use cases for create-drop: integration tests, local development, and CI/CD pipelines that build from scratch. The key is to isolate this setting to test scopes. Use @TestPropertySource or @DataJpaTest with auto-configured test databases. Spring Boot's @DataJpaTest automatically uses an embedded database with ddl-auto=create-drop by default, which is safe because the database is in-memory and temporary. For integration tests that use a real database, you can override the property in the test class. Never let this leak to production. Here's a pattern for safe testing.
The Hibernate 6.x Changes You Need to Know
Hibernate 6.x (shipped with Spring Boot 3.x) introduced changes to schema generation. The 'hibernate.hbm2ddl.auto' property now has a new value: 'drop-and-create' which is an alias for 'create-drop'. More importantly, Hibernate 6.3+ changed the default for non-embedded databases to 'none'. However, the 'create-drop' behavior is still present and just as dangerous. Additionally, Hibernate 6.x uses a new SchemaManager API that can execute DDL in batches. The drop behavior is now more aggressive: it uses CASCADE to drop tables with foreign key constraints, which can silently remove related tables. Also, Hibernate 6.x introduced 'hibernate.schema_management.drop_strategy' which can be configured, but the default is still to drop all tables. Let's see how to inspect the actual Hibernate configuration.
Advanced: Custom SchemaExport for Controlled Drops
If you absolutely must use Hibernate's schema generation in production (which I don't recommend), you can customize the SchemaExport behavior. Hibernate 6.x allows you to implement a custom SchemaExportDelegate that can intercept DROP statements. You could, for example, log the DROP statements instead of executing them, or require an explicit flag to allow drops. This is an advanced technique for teams that have legacy systems tied to Hibernate's schema generation. However, it's a band-aid. The real solution is Flyway. Here's how to implement a custom schema exporter that prevents drops.
Production Checklist: Protecting Your Data
Here's a battle-tested checklist for preventing ddl-auto disasters. First, never set ddl-auto to 'create-drop' or 'create' in any file under src/main/resources. Second, add a startup guard (like the one in Section 2) that crashes the application if these values are used in non-dev profiles. Third, use environment variables for the active profile, not hardcoded values. Fourth, implement a CI/CD pipeline check that scans for forbidden property values. Fifth, use Flyway or Liquibase for all schema changes. Sixth, enable Spring Boot's 'spring.jpa.hibernate.ddl-auto=validate' to catch entity-schema mismatches early. Seventh, monitor your database for unexpected DROP statements using database audit logs. Eighth, have a backup strategy that includes point-in-time recovery. Let's see a complete configuration for production.
Payment Processing Outage Due to ddl-auto=create-drop
- Never use ddl-auto=create-drop in production environments.
- Externalize database schema management to migration tools like Flyway or Liquibase.
- Use Spring profiles to enforce different settings per environment (dev, staging, prod).
- Add a startup check that fails the application if ddl-auto is set to create or create-drop in production.
kubectl rollout undo deployment/myapppsql -h prod-db -U admin -c "SELECT datname FROM pg_database;"| File | Command / Code | Purpose |
|---|---|---|
| DemoApplication.java | @SpringBootApplication | How ddl-auto Works Under the Hood |
| DdlAutoValidator.java | @Component | What the Official Docs Won't Tell You |
| application.properties (base) | spring.jpa.hibernate.ddl-auto=validate | Spring Profiles to the Rescue |
| pom.xml (dependency) | Migration to Flyway | |
| OrderRepositoryTest.java | @SpringBootTest | Testing with create-drop Safely |
| HibernateConfigInspector.java | @Component | The Hibernate 6.x Changes You Need to Know |
| SafeSchemaExport.java | public class SafeSchemaExport extends SchemaExport { | Advanced |
| application-prod.properties | spring.jpa.hibernate.ddl-auto=validate | Production Checklist |
Key takeaways
Interview Questions on This Topic
Explain the difference between ddl-auto=create-drop and ddl-auto=validate in Spring Boot JPA.
Frequently Asked Questions
20+ years shipping production Java in banking & fintech. Drawn from code that ran under real load.
That's Advanced Java. Mark it forged?
4 min read · try the examples if you haven't