diff --git a/compose/db.yml b/compose/db.yml
index 1b85cfe57..b23fe2b73 100644
--- a/compose/db.yml
+++ b/compose/db.yml
@@ -5,7 +5,8 @@ services:
db:
condition: service_healthy
environment:
- QUARKUS_HIBERNATE_ORM_DATABASE_GENERATION: ${DATABASE_GENERATION:-drop-and-create}
+ QUARKUS_HIBERNATE_ORM_DATABASE_GENERATION: ${DATABASE_GENERATION:-none}
+ QUARKUS_HIBERNATE_ORM_SQL_LOAD_SCRIPT: ${SQL_LOAD_SCRIPT:-no-file}
QUARKUS_DATASOURCE_USERNAME: cryostat
QUARKUS_DATASOURCE_PASSWORD: cryostat
QUARKUS_DATASOURCE_JDBC_URL: jdbc:postgresql://db:5432/cryostat
diff --git a/pom.xml b/pom.xml
index f277ae30b..d581b1c46 100644
--- a/pom.xml
+++ b/pom.xml
@@ -274,6 +274,10 @@
${io.fabric8.client.version}
+
+ io.quarkus
+ quarkus-flyway
+
io.quarkus
quarkus-junit5
diff --git a/src/main/resources/application-dev.properties b/src/main/resources/application-dev.properties
index 1c45fc6f0..f995e8829 100644
--- a/src/main/resources/application-dev.properties
+++ b/src/main/resources/application-dev.properties
@@ -20,6 +20,7 @@ cryostat.discovery.kubernetes.enabled=true
quarkus.datasource.devservices.enabled=true
quarkus.datasource.devservices.image-name=quay.io/cryostat/cryostat-db
+quarkus.flyway.clean-at-start=true
# !!! prod databases must set this configuration parameter some other way via a secret !!!
quarkus.datasource.devservices.container-env.PG_ENCRYPT_KEY=examplekey
diff --git a/src/main/resources/application-test.properties b/src/main/resources/application-test.properties
index 28d180f32..261585f9b 100644
--- a/src/main/resources/application-test.properties
+++ b/src/main/resources/application-test.properties
@@ -10,6 +10,7 @@ quarkus.test.env.JAVA_OPTS_APPEND=-Dquarkus.http.host=0.0.0.0 -Djava.util.loggin
quarkus.datasource.devservices.enabled=true
quarkus.datasource.devservices.image-name=quay.io/cryostat/cryostat-db
quarkus.hibernate-orm.log.sql=true
+quarkus.flyway.clean-at-start=true
quarkus.cache.enabled=false
cryostat.services.reports.memory-cache.enabled=false
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 10d21479a..0d1e43396 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,3 +1,7 @@
+quarkus.flyway.baseline-on-migrate=true
+quarkus.flyway.baseline-version=4.0.0
+quarkus.flyway.migrate-at-start=true
+
quarkus.naming.enable-jndi=true
cryostat.discovery.jdp.enabled=false
cryostat.discovery.containers.poll-period=10s
diff --git a/src/main/resources/db/migration/V4.0.0__cryostat.sql b/src/main/resources/db/migration/V4.0.0__cryostat.sql
new file mode 100644
index 000000000..b6d0bfce8
--- /dev/null
+++ b/src/main/resources/db/migration/V4.0.0__cryostat.sql
@@ -0,0 +1,123 @@
+
+ create sequence ActiveRecording_SEQ start with 1 increment by 50;
+
+ create sequence Credential_SEQ start with 1 increment by 50;
+
+ create sequence DiscoveryNode_SEQ start with 1 increment by 50;
+
+ create sequence MatchExpression_SEQ start with 1 increment by 50;
+
+ create sequence Rule_SEQ start with 1 increment by 50;
+
+ create sequence Target_SEQ start with 1 increment by 50;
+
+ create table ActiveRecording (
+ id bigint not null,
+ continuous boolean not null,
+ duration bigint not null,
+ external boolean not null,
+ maxAge bigint not null,
+ maxSize bigint not null,
+ metadata jsonb,
+ name varchar(255),
+ remoteId bigint not null,
+ startTime bigint not null,
+ state smallint check (state between 0 and 4),
+ toDisk boolean not null,
+ target_id bigint,
+ primary key (id),
+ constraint UKr8nr64n7i34ipp019xrbbbyeh unique (target_id, remoteId)
+ );
+
+ create table Credential (
+ id bigint not null,
+ password bytea,
+ username bytea,
+ matchExpression bigint unique,
+ primary key (id)
+ );
+
+ create table DiscoveryNode (
+ id bigint not null,
+ labels jsonb,
+ name varchar(255) not null,
+ nodeType varchar(255) not null,
+ parentNode bigint,
+ primary key (id)
+ );
+
+ create table DiscoveryPlugin (
+ id uuid not null,
+ builtin boolean not null,
+ callback varchar(255) unique,
+ credential_id bigint unique,
+ realm_id bigint not null unique,
+ primary key (id)
+ );
+
+ create table MatchExpression (
+ id bigint not null,
+ script varchar(255) not null,
+ primary key (id)
+ );
+
+ create table Rule (
+ id bigint not null,
+ archivalPeriodSeconds integer not null,
+ description varchar(255),
+ enabled boolean not null,
+ eventSpecifier varchar(255) not null,
+ initialDelaySeconds integer not null,
+ maxAgeSeconds integer not null,
+ maxSizeBytes integer not null,
+ name varchar(255) unique,
+ preservedArchives integer not null,
+ matchExpression bigint unique,
+ primary key (id)
+ );
+
+ create table Target (
+ id bigint not null,
+ alias varchar(255),
+ annotations jsonb,
+ connectUrl bytea unique,
+ jvmId varchar(255),
+ labels jsonb,
+ discoveryNode bigint unique,
+ primary key (id)
+ );
+
+ alter table if exists ActiveRecording
+ add constraint FK2g1pb3osnf0t9g12wnqfjn2a
+ foreign key (target_id)
+ references Target;
+
+ alter table if exists Credential
+ add constraint FKr2h1f9wrs2kcyfwkbtyiux4dn
+ foreign key (matchExpression)
+ references MatchExpression;
+
+ alter table if exists DiscoveryNode
+ add constraint FKhercarglk8snpmw10it6wk6ri
+ foreign key (parentNode)
+ references DiscoveryNode;
+
+ alter table if exists DiscoveryPlugin
+ add constraint FKmxng3svpr3dcm05kfiqekc3ti
+ foreign key (credential_id)
+ references Credential;
+
+ alter table if exists DiscoveryPlugin
+ add constraint FK81w40s7947qra1cgikpbx55mg
+ foreign key (realm_id)
+ references DiscoveryNode;
+
+ alter table if exists Rule
+ add constraint FKosnitp3nlbo5j05my09puf3ij
+ foreign key (matchExpression)
+ references MatchExpression;
+
+ alter table if exists Target
+ add constraint FKl0dhd7qeayg54dcoblpww6x34
+ foreign key (discoveryNode)
+ references DiscoveryNode;