AEM and Apache Derby for local development databases

A local AEM environment should make application development fast, repeatable, and safe. Developers need to start an author instance, install code, create test content, inspect logs, and reset experimental data without waiting for shared infrastructure. A small relational database can support that workflow when the application includes custom services, integrations, or data models outside the content repository.

Apache Derby is useful in this role because it can run in an embedded JVM or as a lightweight network database. It has a small footprint, works with JDBC, and avoids the operational overhead of a full database server during early development. Its role must be defined carefully, however: Derby is generally a companion database for custom application data, not a replacement for AEM’s content repository.

This distinction matters for architecture, configuration, testing, and deployment. The local database should resemble the contract used by the application while remaining separate from Oak, the JCR repository underlying modern AEM versions. With that boundary in place, developers can use Derby productively without creating assumptions that will fail in staging or production.

Where Derby fits in an AEM stack

AEM stores pages, assets, components, configurations, and other repository-managed content in Oak. A local AEM SDK or author instance typically uses an embedded repository suitable for development. Production deployments may use a supported segment store or a clustered MongoDB-backed arrangement, depending on the AEM version and topology. Apache Derby does not take over those responsibilities.

Derby becomes relevant when an AEM project has relational requirements. Examples include temporary import records, workflow coordination data, integration queues, product lookup results, or a small custom reporting store. An OSGi service can access Derby through JDBC while AEM continues to manage content through Sling Resource APIs, JCR, and Oak.

Keeping these concerns separate prevents a common design error: modeling every data requirement as either repository nodes or a database schema without considering operational ownership. Content authors need repository tooling and permissions, while application records may need transactions, unique constraints, joins, and SQL queries. Derby can provide those relational capabilities for a local profile.

Choosing embedded or network mode

Embedded Derby is usually the simplest option for an individual developer. The database files live in a selected directory, and the application starts Derby inside the same JVM or process boundary. There is no separate service to install, and a clean build can remove the database directory before the next test run.

That convenience comes with a strict limitation: an embedded database is normally owned by one JVM at a time. It is a poor fit for multiple AEM instances sharing the same files, and it should not be treated as a miniature clustered database. File locking and shutdown behavior can also make interrupted local runs more noticeable.

Network Server mode separates the Derby engine from the AEM process. This can help when several local services need to connect to one database or when developers want to reproduce client-server behavior. It still does not make Derby a production equivalent for a high-volume, highly available relational platform. The mode should be selected to match the behavior being tested, not simply because it is available.

Connecting an OSGi application safely

An AEM bundle should access Derby through a service abstraction rather than scattering JDBC code across servlets, models, and workflow steps. A useful design places connection handling, schema checks, transactions, and error translation in a dedicated OSGi service. Consumer code then depends on an interface and can use a different implementation in integration tests or deployment environments.

The Derby JDBC driver must be available to the runtime in a way compatible with the AEM version and bundle class-loading rules. A project may package the driver as an OSGi bundle or provision it through the chosen deployment mechanism. The exact approach depends on the SDK, build tooling, and organizational policy, so the driver should be tested in the actual AEM runtime rather than only from a standalone Java application.

Configuration belongs in run-mode-specific OSGi files or an equivalent environment configuration system. A local profile can define the JDBC URL, credentials, database directory, pool limits, and schema behavior without exposing those values in source code. Production settings should come from secured deployment configuration and should point to the approved database platform, not to a Derby file path.

Comparing local database choices

The best local database depends on what the developer is trying to validate. Derby is convenient for a self-contained Java workflow, while a containerized PostgreSQL or MySQL instance may provide a closer approximation to production behavior. An in-memory database can be even faster for unit tests, though it may hide persistence and transaction issues.

Local option Strengths Limitations Suitable AEM use
Derby embedded Minimal setup, file persistence, Java-friendly Single-process ownership, limited production similarity Small custom services and isolated development
Derby Network Server Client-server behavior, shared local access Extra process and lifecycle management Integration tests involving several local clients
In-memory database Very fast reset, simple test execution Data disappears and SQL behavior may differ Unit and repository-adjacent tests
Containerized production-like database Stronger parity with deployment More setup, resource use, and maintenance Integration and release validation
Oak repository Native AEM content model and authoring tools Not a general relational database Pages, assets, configurations, and JCR content

The comparison also highlights why a single database should not be forced across every test layer. Unit tests can use mocks or an in-memory store. AEM integration tests can use Derby for a lightweight relational dependency. Release candidates should run against the database engine and version that production actually supports.

Designing schemas for disposable environments

A local Derby schema should be easy to create, inspect, and destroy. Versioned migration scripts are preferable to a collection of startup statements hidden in application code. A migration tool or a small, explicit schema manager can record the installed version and apply changes in a predictable order.

Use meaningful primary keys, foreign keys, indexes, and constraints even in development. Loose local rules create false confidence, especially when production enforces stricter data integrity. SQL behavior should also be checked for reserved words, timestamp handling, generated keys, and transaction isolation because these details may differ between Derby and the eventual target database.

Database directories should be excluded from source control and treated as disposable artifacts. Seed data can be generated through a repeatable script, test fixture, or content package, while sensitive data should never be copied into a developer workstation. A reset command that stops the local service, removes the schema, and rebuilds fixtures is often more valuable than preserving a damaged development database.

Testing failure and lifecycle behavior

A successful connection proves very little. Tests should cover unavailable databases, invalid credentials, locked rows, duplicate keys, rollback behavior, connection exhaustion, and application shutdown. AEM services must fail in a controlled manner rather than blocking request threads indefinitely when Derby is stopped or unavailable.

Connection pools deserve particular attention. Pool sizes that work for a single developer can conceal leaks or create unrealistic behavior. Every connection, statement, and result set should be closed reliably, preferably through structured resource handling. Transactions should be short, explicit, and aligned with the service operation that needs atomicity.

The AEM lifecycle introduces additional concerns. Services may activate before the database is ready, and configuration changes may cause them to reactivate. Activation should validate configuration and report a useful health state. It should not silently create a production-like schema with unsafe defaults or leave partially initialized tables after a failed migration.

Making local development reproducible

A reliable setup documents the AEM runtime, Java version, Derby version, driver provisioning method, database location, startup commands, and reset procedure. These details should be encoded where possible in Maven profiles, repository scripts, container definitions, or developer tooling. Documentation alone is helpful, but automation reduces differences between workstations.

The conference materials and recorded technical sessions available through the event agenda can provide useful context for how AEM teams approach architecture, integrations, and local experimentation. A Derby-backed service is most valuable when it supports those broader engineering practices instead of becoming an undocumented shortcut.

A practical local workflow can follow this sequence:

  • Start the AEM author instance with a clearly selected development run mode.
  • Start Derby in embedded or network mode according to the test scenario.
  • Apply versioned migrations and load safe, deterministic fixtures.
  • Exercise the OSGi service through unit, integration, and authoring workflows.
  • Stop, reset, and rebuild the database regularly to verify repeatability.

This process keeps repository content, relational records, and test fixtures visible as separate assets. It also makes failures easier to diagnose because the team can identify whether a problem belongs to Sling or JCR access, JDBC configuration, schema state, or the database lifecycle.

Moving from experiment to team practice

Apache Derby is a practical local companion for AEM applications that need relational storage during development. Its small footprint and Java integration make it suitable for focused services, migration logic, and integration tests. Its limitations are equally important: it should not be presented as the persistence layer for Oak or as a substitute for the production database.

Before adopting the setup across a project, document the supported AEM and Java versions, define the local database contract, automate schema creation, and test the deployment profile against the real target engine. The FAQ resources can help clarify event and platform context while the team turns conference ideas into an executable development workflow.

Build the Derby integration as a replaceable service, keep local data disposable, and make startup and reset commands part of the project’s normal toolchain. That approach gives developers a fast AEM environment today while preserving a clear path to reliable staging and production deployments.