Can't run flyway migration in my ktor microservice

nickbeginner
HOBBY

6 days ago

i'm not able to run ./gradlew flywayMigrate tastk during pre-deploy how can i do it? here my dockerfile and my build.gradle

# Stage 1: Build Kotlin app with Gradle FROM gradle:8-jdk21 AS build COPY --chown=gradle:gradle . /home/gradle/src WORKDIR /home/gradle/src RUN gradle buildFatJar --no-daemon # Stage 2: Generate openapi.json with Redocly CLI FROM node:18-alpine AS redocly-builder WORKDIR /app COPY --from=build /home/gradle/src/openapi-gen ./openapi-gen RUN npm install -g @redocly/cli RUN redocly bundle openapi-gen/documentation.yaml --output openapi.json # Stage 3: Final runtime image FROM eclipse-temurin:21-jdk-jammy EXPOSE 8080 RUN mkdir /app WORKDIR /app # Copy Kotlin fat JAR COPY --from=build /home/gradle/src/build/libs/displate-companies.jar /app/displate-companies.jar # Copy generated openapi.json COPY --from=redocly-builder /app/openapi.json /app/openapi.json CMD ["/bin/sh", "-c", "java -jar /app/displate-companies.jar"]

import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar import io.gitlab.arturbosch.detekt.Detekt import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask plugins { application alias(libs.plugins.kotlin.serialization) alias(libs.plugins.kotlin.jvm) alias(libs.plugins.sqldelight) alias(libs.plugins.flyway) alias(libs.plugins.detekt) alias(libs.plugins.ktor) id("com.github.johnrengelman.shadow") version "8.1.1" } group = "capibara.org" version = "0.0.1" kotlin { jvmToolchain { languageVersion.set(JavaLanguageVersion.of(21)) vendor.set(JvmVendorSpec.ADOPTIUM) } } buildscript { dependencies { classpath(libs.postgresql) classpath(libs.flyway.postgresql) } } application { mainClass.set("io.ktor.server.netty.EngineMain") applicationDefaultJvmArgs = listOf( "-Dio.ktor.development=${project.findProperty("development") ?: "false"}", "-XX:+UseContainerSupport" ) sqldelight { databases { create("DisplateDatabase") { packageName.set("org.capibara") dialect("app.cash.sqldelight:postgresql-dialect:2.0.2") deriveSchemaFromMigrations.set(true) migrationOutputDirectory = file("src/main/kotlin/db/migrations") migrationOutputFileFormat = ".sql" } } } tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> { dependsOn("generateMainDisplateDatabaseMigrations") } tasks.getByName("detekt").mustRunAfter("generateMainDisplateDatabaseMigrations") tasks.withType<Detekt>().configureEach { reports { html.required.set(true) xml.required.set(true) txt.required.set(true) sarif.required.set(true) md.required.set(true) } } tasks.withType<Detekt>().configureEach { jvmTarget = "1.8" } tasks.withType<DetektCreateBaselineTask>().configureEach { jvmTarget = "1.8" } tasks.withType<ShadowJar> { manifest { attributes( "Main-Class" to application.mainClass.get(), "Implementation-Version" to project.version ) } archiveFileName.set("displate-companies.jar") } tasks.register("stage") { dependsOn("shadowJar") } flyway { user = System.getenv("POSTGRES_USER") password = System.getenv("POSTGRES_PASSWORD") url = System.getenv("DATABASE_URL") locations = arrayOf("filesystem:src/main/kotlin/db/migrations") baselineOnMigrate = true baselineVersion = "1" sqlMigrationPrefix = "v" } }

$10 Bounty

3 Replies

mycodej
HOBBYTop 10% Contributor

6 days ago

You won’t be able to run flywayMigrate during the Docker image build stage on Railway because the build phase doesn't have access to your Railway Postgres instance—it's only available at runtime, once the container is running.

The recommended approach is to run the flywayMigrate task as part of your container’s startup command. Here's how you can adjust your Dockerfile:

# ... keep your build and redocly stages as is

# Runtime stage

FROM eclipse-temurin:21-jdk-jammy

WORKDIR /app

# Copy fat jar

COPY --from=build /home/gradle/src/build/libs/displate-companies.jar .

# Copy gradle wrapper and migration scripts

COPY --from=build /home/gradle/src/gradlew .

COPY --from=build /home/gradle/src/gradle ./gradle

COPY --from=build /home/gradle/src/src/main/kotlin/db/migrations ./src/main/kotlin/db/migrations

RUN chmod +x ./gradlew

CMD /bin/sh -c "./gradlew -p /app flywayMigrate --no-daemon && java -jar displate-companies.jar"

This setup runs the migration at startup when the container has access to your Railway-provided environment variables and database. Flyway is idempotent, so this is safe to run on every deploy—it'll just skip if there are no new migrations.

Let me know if you need help adapting this further!


sim
FREETop 1% Contributor

3 days ago

What does your pre deploy look like?


mycodej
HOBBYTop 10% Contributor

2 days ago

Since Railway doesn't support a separate pre-deploy step for Docker services with access to the internal Postgres instance, I handle database migrations during container startup instead.

In my Dockerfile, I include the flywayMigrate Gradle task in the container’s CMD, like this:

CMD /bin/sh -c "./gradlew -p /app flywayMigrate --no-daemon && java -jar displate-companies.jar"

This ensures the migration runs just before the app starts, and it works because the environment variables for the database (like DATABASE_URL, POSTGRES_USER, POSTGRES_PASSWORD) are only available at runtime—not during the image build.

Flyway is safe to run like this since it will skip already-applied migrations, so even if the container restarts, it won’t reapply anything.


Can't run flyway migration in my ktor microservice - Railway Help Station