Mirth stores every message, every transformation, and every audit event in a relational database. Defaults are fine in dev and fall over in production. Use PostgreSQL (or SQL Server if it's already your hospital standard) — never Derby in production. Size the connection pool to expected concurrent channels + 10. Enable per-channel pruning from day one: 7–30 days for high-volume routine traffic, 30–90 days for clinical, 90+ days for compliance-sensitive. Back up daily with continuous WAL/log shipping and test restores quarterly. Run the database on a dedicated host (or managed service) with SSD storage for any deployment beyond the smallest pilot.
Why this matters
The database is where most Mirth Connect deployments quietly fail. Defaults work in development and for the first six months of production. Then channels start getting slower, deploys start hanging, and queries that used to return in milliseconds take seconds. The team blames Mirth, blames the JVM, blames the network — and the actual cause is a database that has been growing without pruning, running on undersized hardware, or using settings that were appropriate for a 100 MB database and have been carried into a 500 GB one.
This guide is the production-grade database playbook we apply in client engagements. It's organized so you can either set up a new Mirth database correctly from day one, or remediate an existing deployment that is already in trouble. Pair it with our companion guides — Mirth Connect performance tuning, Mirth Connect Java heap space error, and the broader Mirth Connect complete guide. For productized engagement options, see Mirth Connect support.
1. What Mirth stores in its database
Understanding what the database holds explains why it grows so fast and what to do about it.
For each message that flows through any Mirth channel, the engine writes:
- Message metadata — channel, server, message ID, timestamps, status, source/destination identifiers
- Raw content — the original message bytes as received
- Transformed content — the message after each transformer in the pipeline
- Connector messages — per-connector copies of the data
- Custom metadata — anything you store via custom metadata columns
- Errors — any exception thrown during processing
A single inbound HL7 message with one source connector and three destinations can produce 8–12 rows across multiple tables. Multiply by your message volume.
Beyond messages, Mirth also stores:
- Channel configurations — every channel's full XML configuration, plus version history
- User accounts and authentication data
- Code templates and libraries
- Configuration map values
- Audit log entries — every UI action, login, channel deploy
- Statistics and metrics — message counts per channel per time bucket
A busy hospital running 20 active HL7 channels with 500,000 messages per day, with default storage settings and no pruning, will write roughly 3–6 GB per day to the database. A reference lab moving 5 million messages per day can hit 50–100 GB per day. Without pruning, the database doubles every 30–60 days. This is why retention strategy is a first-class concern, not an afterthought — it is the difference between stable production and a database that becomes unmanageable.
2. Choosing a database: PostgreSQL, SQL Server, MySQL, or Oracle
Mirth supports four production-grade databases. The right choice depends on your existing operations, licensing budget, and operational expertise.
PostgreSQL — recommended default. The most common choice we see in 2026 Mirth deployments. Free, open source, mature, with excellent performance characteristics for Mirth's workload pattern. Great cloud support across AWS RDS, Azure Database for PostgreSQL, and Google Cloud SQL. Strong observability tooling and a vast operator community. Pick PostgreSQL when you have no preexisting database commitment, want to minimize licensing cost, or are deploying cloud-native.
Microsoft SQL Server. A very common choice in US hospital IT environments where SQL Server is already the standard for clinical and administrative systems. Mirth runs well on SQL Server, and the operational tooling (SSMS, integrated backup, Always On availability groups) is mature. Pick SQL Server when you already operate it as a hospital standard, have DBA skills concentrated there, or have enterprise licensing already in place.
MySQL. Supported by Mirth and used in some deployments, but less common than the other three for production. Performance is adequate. The main reason to pick MySQL today is preexisting investment in the platform. Pick MySQL when you already operate it confidently and have the DBA expertise. For greenfield deployments, PostgreSQL is usually a better choice.
Oracle. The least common in 2026 Mirth deployments — primarily seen in large enterprise hospitals where Oracle is the existing data platform standard. Mirth runs on Oracle correctly, but the licensing cost is meaningful and the hiring premium for Oracle DBAs is real. Pick Oracle when organizational standards require it, you have negotiated favorable Oracle licensing already, or your team has deep Oracle expertise concentrated in the integration platform.
| Factor | PostgreSQL | SQL Server | MySQL | Oracle |
|---|---|---|---|---|
| License cost | Free | Per-core licensed | Free / paid | Per-core licensed |
| Performance | Excellent | Excellent | Good | Excellent |
| Cloud managed | RDS / Azure / Cloud SQL | RDS / Azure SQL | RDS / Azure / Cloud SQL | OCI / RDS Oracle |
| Hospital prevalence (US) | Growing fast | Very high | Low | Moderate (legacy) |
| DBA hiring pool | Large | Very large | Large | Smaller / premium |
| Best fit | Greenfield, healthtech | Hospital IT existing standard | Existing investment | Enterprise standard |
For broader engine-level context, see our performance tuning guide — most database tuning advice in this article complements the engine-level guidance there.
3. Why Derby is not a production option
Apache Derby ships with Mirth as the embedded default database. It's appropriate for development, demos, and evaluation — and inappropriate for any production workload, no matter how small.
Why not Derby:
- Single-process embedded mode — only the Mirth process can access the database. No external administration, no monitoring tools, no third-party backup agents.
- Performance collapses at relatively low message volumes. We've seen Derby-based Mirth deployments crash at 200K messages — a routine day for a small hospital.
- No production-grade backup/restore workflow.
- No built-in observability for query performance, locking, or capacity.
- Difficult to scale or migrate once data starts to accumulate.
What to do if you're on Derby in production: migrate immediately. Section 11 of this guide covers Derby-to-PostgreSQL migration step by step. The longer you wait, the larger the dataset and the more painful the migration. Most clients we encounter on production Derby are weeks away from a serious incident.
4. Initial setup — PostgreSQL walkthrough
This is the most common production setup. Adapt the commands to your operating system.
4.1 Install PostgreSQL. On Ubuntu / Debian:
sudo apt update sudo apt install postgresql postgresql-contrib sudo systemctl enable postgresql sudo systemctl start postgresql
On RHEL / CentOS:
sudo dnf install postgresql-server postgresql-contrib sudo postgresql-setup --initdb sudo systemctl enable postgresql sudo systemctl start postgresql
For production: run PostgreSQL on a dedicated server or use a managed service (AWS RDS, Azure Database for PostgreSQL, Google Cloud SQL).
4.2 Create the Mirth database and user.
sudo -u postgres psql
In the psql shell:
CREATE DATABASE mirthdb; CREATE USER mirthuser WITH ENCRYPTED PASSWORD 'STRONG_PASSWORD_HERE'; GRANT ALL PRIVILEGES ON DATABASE mirthdb TO mirthuser; ALTER DATABASE mirthdb OWNER TO mirthuser; \q
4.3 Configure PostgreSQL connection settings. Edit /etc/postgresql/<VERSION>/main/postgresql.conf:
listen_addresses = '*' # or specific interface port = 5432 max_connections = 200
Edit /etc/postgresql/<VERSION>/main/pg_hba.conf to allow Mirth's connection. For Mirth on the same host:
host mirthdb mirthuser 127.0.0.1/32 scram-sha-256
For Mirth on a separate host:
host mirthdb mirthuser 10.0.0.0/24 scram-sha-256
Restart PostgreSQL:
sudo systemctl restart postgresql
4.4 Configure Mirth to use PostgreSQL. Edit /opt/mirthconnect/conf/mirth.properties:
database = postgres database.url = jdbc:postgresql://db-host:5432/mirthdb database.driver = org.postgresql.Driver database.username = mirthuser database.password = STRONG_PASSWORD_HERE # Connection pool database.pool.maxconnections = 60 database.pool.maxidletime = 60000 database.pool.connectiontestquery = SELECT 1
For password security, Mirth supports password encryption — see the Mirth documentation for the current encrypt.properties workflow.
4.5 Start Mirth. On first start with an empty database, Mirth automatically creates the schema and core tables. Watch the log:
tail -f /opt/mirthconnect/logs/mirth.log
Look for “Database initialized” or equivalent. If you see schema-creation errors, the most common causes are insufficient privileges (re-grant ALL on the database) or wrong driver class.
4.6 Verify the schema.
\c mirthdb \dt
You should see tables like channel, event, d_channels, d_metadata_columns, d_<channel-id> (created when channels are deployed), and others. The schema is now ready. For complete installation context, see our Mirth Connect installation guide.
5. Initial setup — SQL Server walkthrough
For environments standardizing on Microsoft SQL Server.
5.1 Create database and login. In SSMS or via T-SQL:
CREATE DATABASE mirthdb GO CREATE LOGIN mirthuser WITH PASSWORD = 'STRONG_PASSWORD_HERE' GO USE mirthdb GO CREATE USER mirthuser FOR LOGIN mirthuser ALTER ROLE db_owner ADD MEMBER mirthuser GO
5.2 Configure SQL Server for remote connections. Ensure:
- TCP/IP protocol enabled in SQL Server Configuration Manager.
- SQL Server service restarted after changes.
- Firewall rule for port 1433 (or your custom port).
- Authentication mode allows SQL authentication if you're using SQL logins (not just Windows auth).
5.3 Configure Mirth to use SQL Server. Edit /opt/mirthconnect/conf/mirth.properties:
database = sqlserver database.url = jdbc:sqlserver://db-host:1433;databaseName=mirthdb;encrypt=true;trustServerCertificate=false database.driver = com.microsoft.sqlserver.jdbc.SQLServerDriver database.username = mirthuser database.password = STRONG_PASSWORD_HERE database.pool.maxconnections = 60
For TLS to the database (recommended in production), ensure SQL Server has a valid certificate and use encrypt=true with proper trust configuration.
5.4 Start Mirth. Same as PostgreSQL — first start creates schema. Verify in SSMS that tables are created in mirthdb.
6. Initial setup — MySQL and Oracle notes
MySQL.
CREATE DATABASE mirthdb CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER 'mirthuser'@'%' IDENTIFIED BY 'STRONG_PASSWORD_HERE'; GRANT ALL PRIVILEGES ON mirthdb.* TO 'mirthuser'@'%'; FLUSH PRIVILEGES;
mirth.properties:
database = mysql database.url = jdbc:mysql://db-host:3306/mirthdb?useUnicode=true&characterEncoding=UTF-8 database.driver = com.mysql.cj.jdbc.Driver database.username = mirthuser database.password = STRONG_PASSWORD_HERE
Make sure character_set_server = utf8mb4 and collation_server = utf8mb4_unicode_ci in my.cnf to handle international characters correctly — common issue with HL7 patient names.
Oracle.
CREATE USER mirthuser IDENTIFIED BY "STRONG_PASSWORD_HERE"; GRANT CONNECT, RESOURCE, CREATE SESSION TO mirthuser; GRANT UNLIMITED TABLESPACE TO mirthuser;
mirth.properties:
database = oracle database.url = jdbc:oracle:thin:@db-host:1521:ORCL database.driver = oracle.jdbc.OracleDriver database.username = mirthuser database.password = STRONG_PASSWORD_HERE
Mirth ships with appropriate JDBC drivers for each database in /server-lib/database/. If you need to update the driver to a specific version, drop the new JAR in the same location.
7. Connection pool configuration
Mirth pools its database connections. Pool sizing affects everything from channel deploy speed to message throughput under load.
Sizing the pool. Rule of thumb: maxconnections = expected concurrent channels + 10. For a 50-channel deployment, 60 connections is a reasonable starting point.
Considerations:
- Too small — channels stall waiting for connections during peaks; deploys queue.
- Too large — wastes database server resources; can hit database server's
max_connectionslimit when multiple Mirth nodes share a database. - Database server limit — PostgreSQL
max_connections = 200is a typical baseline; in clustered Mirth setups, sum the pool sizes across all nodes.
Pool configuration in mirth.properties:
database.pool.maxconnections = 60 database.pool.maxidletime = 60000 # ms — close idle connections after 1 minute database.pool.connectiontestquery = SELECT 1 database.pool.evictionperiod = 60000 # ms — frequency of pool eviction checks
Connection timeouts. Add explicit timeouts to prevent hung database calls from cascading into channel hangs:
database.pool.connectiontimeout = 30000 # ms — max wait for a connection database.pool.maxlifetime = 1800000 # ms — max connection age (30 min) before forced reset
These help recover gracefully from database failovers and network blips.
8. Retention and pruning strategy
The single most important lever for long-term database health. Without retention, the database grows until it breaks something.
8.1 Channel-level pruning. In the Mirth Administrator, for each channel: Channel → Set Pruning Settings. Configure two thresholds:
- Prune messages older than N days — removes messages and metadata.
- Prune content older than N days — removes the content blobs (raw, processed) but retains metadata.
Pruning content first and metadata later is a useful pattern — you keep auditable evidence of message flow longer than you keep the actual PHI payload.
8.2 Recommended retention by channel type:
| Channel type | Content retention | Metadata retention |
|---|---|---|
| High-volume routine (ADT, lab results) | 7 days | 30 days |
| Mid-volume clinical (orders, scheduling) | 30 days | 90 days |
| Compliance-sensitive (state reporting, immunization) | 90 days | 1 year |
| Low-volume audit / financial | 90+ days | 1+ years |
These are starting points. Adjust based on your specific compliance requirements (HIPAA, state law, payer contract). Consult our healthcare interoperability and compliance guide for the broader regulatory context.
8.3 External archival. The Mirth database is nota long-term compliance archive. For data that must be retained for years (litigation hold, regulatory retention requirements), export to an object storage bucket (S3, Azure Blob, GCS) with lifecycle policies, a clinical data warehouse, or a dedicated archive system with compliance certifications. Mirth's pruning happens locally; long-term retention is a separate architectural concern.
8.4 Pruning performance. Pruning runs as a scheduled job within Mirth. On large tables (hundreds of millions of rows), the first prune after enabling can take hours and consume significant database resources. Best practice:
- Enable pruning early — before tables grow large, retention is cheap to apply.
- Schedule pruning during off-hours — Mirth lets you configure the window.
- For initial cleanup of an already-large database, consider a one-time bulk delete script run by your DBA outside Mirth's pruning system, with appropriate vacuum/index-rebuild afterward.
If you've inherited a Mirth deployment with no pruning ever configured, the first cleanup is essentially a database administration project — plan for it accordingly.
9. Backup and disaster recovery
Mirth's database contains every channel configuration and every PHI message that flowed through. Losing it means a disastrous integration outage and a likely HIPAA breach. Backup is non-negotiable.
9.1 What to back up:
- Mirth database — full and incremental backups
/opt/mirthconnect/conf/—mirth.properties, keystores, configuration files/opt/mirthconnect/custom-lib/— any custom Java libraries- Channel configuration exports — also export channels to Git as a parallel backup with version history
9.2 Backup schedule (typical production):
| Backup type | Frequency | Retention |
|---|---|---|
| Database full | Daily | 7–30 days |
| Database differential (where supported) | Every 6–12 hours | 7 days |
| Database transaction log / WAL | Continuous (every 5–15 min) | 7 days |
Configuration / /opt/mirthconnect/conf/ | Weekly + before each change | 90+ days |
| Channel exports to Git | On every channel change | Permanent |
For PostgreSQL: use pg_dump for full backups and pg_basebackup + WAL archiving for point-in-time recovery. For SQL Server: native backup with full / differential / log strategy.
9.3 Test restores quarterly.A backup that has never been restored is not a backup — it's a hope. Quarterly: restore the database to a non-production environment, stand up Mirth pointing at the restored database, verify that channels deploy and that messages process correctly, and document the recovery time achieved.
9.4 Disaster recovery RTO and RPO. Define explicit targets:
- Recovery Time Objective (RTO) — how long can the integration platform be down? Typical target: under 4 hours for active hospital workflows.
- Recovery Point Objective (RPO) — how much message loss is acceptable? Typical target: under 15 minutes of data loss.
Architectural choices (synchronous replication, hot standby, multi-region) follow from these targets. Discuss with leadership before incidents force the decision. For broader architecture and DR patterns specific to Mirth, see the deployment patterns in our Mirth Connect complete guide.
10. Performance tuning the database
Beyond initial setup, the database needs operational tuning at production scale.
10.1 PostgreSQL starting points. postgresql.conf (for a dedicated 16 GB RAM database server):
shared_buffers = 4GB # ~25% of RAM effective_cache_size = 12GB # ~75% of RAM work_mem = 32MB maintenance_work_mem = 1GB wal_buffers = 16MB checkpoint_timeout = 15min max_wal_size = 4GB random_page_cost = 1.1 # if SSD effective_io_concurrency = 200 # if SSD max_connections = 200
Tune to your specific workload — these are starting points, not prescriptions.
10.2 Index health. Mirth ships with sensible default indexes. In high-volume environments, monitor:
-- Find unused indexes (PostgreSQL) SELECT schemaname, tablename, indexname, idx_scan FROM pg_stat_user_indexes WHERE idx_scan = 0 ORDER BY pg_total_relation_size(schemaname || '.' || indexname) DESC LIMIT 20; -- Find missing index opportunities SELECT relname, seq_scan, idx_scan, seq_tup_read FROM pg_stat_user_tables WHERE seq_scan > idx_scan AND seq_tup_read > 1000000 ORDER BY seq_tup_read DESC;
Don't add indexes blindly — they cost write performance. Add only when query patterns clearly benefit.
10.3 Vacuum and autovacuum. PostgreSQL relies on VACUUM to reclaim space from deleted rows. With heavy pruning workloads, autovacuum may struggle to keep up:
autovacuum_max_workers = 4 autovacuum_naptime = 30s autovacuum_vacuum_scale_factor = 0.05 autovacuum_analyze_scale_factor = 0.025
Monitor autovacuum activity in pg_stat_user_tables (column last_autovacuum) and adjust if specific high-churn tables aren't being vacuumed often enough.
10.4 Storage and I/O:
- Use SSD or NVMe storage. Spinning disks are not appropriate for production Mirth.
- Use a dedicated database volume separate from the OS and Mirth application volumes.
- For cloud: provisioned IOPS matters. Burst-throughput volumes will throttle during pruning windows.
10.5 Database server isolation. For high-volume deployments, run the database on a dedicated server rather than co-located with Mirth. This eliminates resource contention and lets you scale each tier independently. For broader engine + database performance work, our Mirth Connect performance tuning guide covers the full stack.
11. Migrating from Derby to a production database
If you're on Derby in production, this is your priority migration. The procedure:
Step 1 — Prepare the new database. Create the database, user, and credentials per Section 4 (PostgreSQL) or Section 5 (SQL Server). Test connectivity.
Step 2 — Export current Mirth configuration.Before any migration, export everything from the running Mirth Administrator: channel exports (Channels → Export Channels), Code Templates exports, Global Scripts, Configuration Map, and user accounts (note these; they don't always export cleanly). Save in a safe location.
Step 3 — Plan the message data migration. Decide whether to migrate historical message data or start fresh on the new database. Most teams start fresh— Derby's data is typically operational state, not compliance evidence, and migrating large Derby tables to PostgreSQL is non-trivial. If historical message data must be retained for compliance, export before the cutover (Mirth supports message export to file) or run a database-level migration with appropriate tooling — usually requiring DBA expertise.
Step 4 — Schedule the cutover. Pick a maintenance window. Plan for 2–4 hours of downtime. Notify upstream senders to expect connectivity issues during the window.
Step 5 — Stop Mirth, switch the database.
# Stop Mirth sudo systemctl stop mirth-connect # Backup current Derby data location (just in case) cp -a /opt/mirthconnect/mirthdb /opt/mirthconnect/mirthdb.derby.bak
Step 6 — Update mirth.properties to point at the new database. Edit database, database.url, database.driver, database.username, database.password per the new database walkthrough.
Step 7 — Start Mirth with the new database.
sudo systemctl start mirth-connect tail -f /opt/mirthconnect/logs/mirth.log
Mirth creates a fresh schema in the new database. Watch for errors.
Step 8 — Re-import configurations. In the Administrator, import: channels (import the XML you exported in Step 2), Code Templates, Global Scripts, Configuration Map values, and recreate user accounts (typically manual).
Step 9 — Test and verify. Deploy each channel and verify it starts cleanly. Send a test message through each interface. Verify message logs in the new database. Compare channel configurations against your exports for completeness.
Step 10 — Monitor closely for the first 48 hours. Watch heap usage, error rates, and channel state. If anything is wrong, you can revert by stopping Mirth, switching mirth.properties back to Derby, and restarting — provided you preserved the Derby data in Step 5.
For broader migration scoping (Derby is just one of several engine-migration scenarios), see our comparison-driven migrations content like Mirth Connect vs Cloverleaf, which includes a similar phase-by-phase framework for engine migrations.
12. Common database issues
The recurring patterns we resolve in client engagements:
12.1 Database growing without bounds. Cause: No pruning enabled. Tables grow indefinitely; performance degrades smoothly. Fix: Enable per-channel pruning (Section 8). For an already-large database, plan a one-time cleanup with DBA support.
12.2 Channel deploys hanging. Cause: Connection pool exhausted; channels can't get a database connection. Often caused by undersized pool combined with a sudden burst of traffic. Fix: Increase database.pool.maxconnections. Check the database server's max_connections to ensure capacity exists.
12.3 Slow queries from Mirth Administrator. Cause: Missing indexes on custom metadata columns, or autovacuum behind on large tables. Fix: Index appropriately. Run VACUUM ANALYZE on lagging tables.
12.4 Locked / blocked sessions. Cause: Long-running queries holding locks. Common during pruning operations on large tables. Fix: Schedule pruning for off-hours. Consider partitioning very large tables (advanced — usually requires DBA work). Tune lock_timeout to fail fast on contended queries.
12.5 Database connection errors at deploy time. Cause: Channel destinations using JDBC connectors with wrong credentials, expired passwords, or bad URLs. Fix: Test the JDBC connection from the Mirth host directly. Update credentials. See Mirth Connect channel not starting for the broader deploy-error troubleshooting framework.
12.6 Out of disk space. Cause: Pruning misconfigured or paused; logs not rotated; backup files accumulating. Fix: Free space immediately (rotate logs, delete old backups), then enable proper pruning, and configure log rotation with logrotate. Set up alerting at 80% disk usage.
12.7 Deadlocks during high-throughput periods. Cause: Concurrent updates to shared tables. Often related to channel state or statistics tables under heavy load. Fix: This is usually a tuning issue — connection pool sizing, channel queue sizing, transaction isolation levels. Often requires both Mirth-side and database-side tuning. Mature support engagement needed if persistent.
For the full operational context, see our Mirth Connect issues and fixes catalog.
Need help with Mirth Connect database setup, tuning, or migration?
Database is the most common bottleneck in Mirth deployments and the single most common silent reason for production slowdowns. Whether you need to migrate from Derby, tune an existing PostgreSQL or SQL Server installation, or design a high-availability architecture for a new deployment — our engineers have done it across more than 100 US healthcare organizations.
The lowest-friction entry point: claim a Free Mirth Health Check — 60 minutes with a senior engineer who reviews your channel inventory, performance, and database posture. Produces a written report with prioritized recommendations and a ballpark effort estimate. No commitment.
For specific productized engagement paths:
- Mirth Installation Sprint — from $4,500, 2 weeks if you need a clean PostgreSQL-backed install from scratch
- Bronze Support — $3,800/mo for ongoing database maintenance on smaller deployments
- Silver Support — $6,800/mo for production-grade database operations with 24/7 monitoring and 15-min critical SLA
- Gold Support — $12,500/mo for HA database architectures and hospital-network-scale workloads
Database performance work fits naturally into any of our support tiers since it's recurring operational work, not a one-time project. See /pricing for the full breakdown.
Related Reading
- Mirth Connect: The Complete Guide →
- Mirth Connect Performance Tuning →
- Mirth Connect Java Heap Space Error →
- Mirth Connect Channel Not Starting →
- Mirth Connect MLLP Connection Refused →
- Mirth Connect Groovy vs JavaScript Transformers →
- Mirth Connect Installation Guide →
- Common Mirth Connect Issues & Fixes →
- Healthcare Interoperability & Compliance Guide →
- HL7 Integration: The Complete Guide →
- Mirth Connect Support Pricing Explained →
- Free Mirth Health Check: What to Expect →