Skip to content

Container AUT (stove-container)

Run the AUT as a Docker image. Any language, any framework, with the same entrypoint and runtime image you ship.

In 30 seconds Replace your framework starter with containerApp(image = "my-app:tag", target = ContainerTarget.Server(...), envProvider = envMapper { ... }). Stove starts the container, maps system configuration into env vars or CLI args, and waits on your readiness probe. Image build is your responsibility, not Stove's.

For a host-binary AUT (process mode) see stove-process. For a Go-specific walkthrough see Go Container Mode.

What stove-container does

  • Pulls or locates the image and wraps it as a Testcontainers GenericContainer
  • Maps Stove infrastructure config to env vars (envMapper) or CLI args (argsMapper)
  • Waits for readiness via your chosen ReadinessStrategy
  • Exposes host ports according to ContainerTarget.Server and your port-binding strategy
  • Graceful shutdown with force-close fallback

What it does NOT do:

  • โŒ Build your image (use Docker, Bazel, ko, jib, whatever)
  • โŒ Manage your registry
  • โŒ Bridge into your app's DI container

Configure

import com.trendyol.stove.container.ContainerTarget
import com.trendyol.stove.container.containerApp
import com.trendyol.stove.system.ReadinessStrategy
import com.trendyol.stove.system.application.envMapper

Stove().with {
  postgresql { /* ... */ }
  kafka      { /* ... */ }

  containerApp(
    image = "ghcr.io/your-org/your-app:local",
    target = ContainerTarget.Server(
      hostPort = 8090,
      internalPort = 8090,
      portEnvVar = "APP_PORT",
      bindHostPort = true,
      readiness = ReadinessStrategy.HttpGet(url = "http://localhost:8090/health")
    ),
    envProvider = envMapper {
      // Left side must match the keys your systems expose.
      "database.host" to "DB_HOST"
      "database.port" to "DB_PORT"
      "database.name" to "DB_NAME"
      "kafka.bootstrapServers" to "KAFKA_BROKERS"
    },
    configureContainer = {
      withFileSystemBind("./fixtures", "/app/fixtures")
    }
  )
}.run()

Target

Variant Use
ContainerTarget.Server(hostPort, internalPort, portEnvVar, bindHostPort = true) App listens on a port; readiness probe required
ContainerTarget.Worker() No port; readiness via Probe or FixedDelay

ContainerTarget.Worker(readiness = ...) accepts no port because workers do not expose a listening socket. Use a stable hostPort when tests call the AUT through localhost. Use hostPort = 0 only when tests can discover the mapped port and readiness does not depend on a fixed localhost URL.

Readiness

ReadinessStrategy.HttpGet(url = "http://localhost:8090/health")
ReadinessStrategy.TcpPort(port = 8090)             // any TCP listener
ReadinessStrategy.Probe { /* boolean */ }
ReadinessStrategy.FixedDelay(5.seconds)            // last resort

Env vs CLI args

// env (most images)
envProvider = envMapper {
  "database.host" to "DB_HOST"
}

// CLI args (some Go / Rust binaries)
argsProvider = argsMapper {
  "database.host" to "db-host"
  "kafka.bootstrapServers" to "kafka"
}

Both can coexist on the same containerApp; use the shape your image already supports.

Networking gotchas

Mode Pros Cons
withNetworkMode("host") App reaches localhost:port directly Linux only
Port binding (default) Cross-platform Stove-managed infra reachable via Testcontainers network alias, not localhost

For port binding, map infrastructure hosts in envProvider to values the container can reach:

envProvider = envMapper {
  "postgresql.host" to "DB_HOST"           // ends up = testcontainers alias
  "postgresql.port" to "DB_PORT"
  "kafka.bootstrapServers" to "KAFKA"
}

Bind mounts

configureContainer = {
  withFileSystemBind(
    File("./fixtures").absolutePath,
    "/app/fixtures",
    BindMode.READ_ONLY
  )
}

Useful for seed data, certificates, schema files.

Coverage / shutdown hooks

beforeStarted hook fires before container start. gracefulShutdownTimeout controls SIGTERM patience before SIGKILL.

containerApp(
  image = "my-app:local",
  /* ... */,
  beforeStarted = { configurations -> /* write config files, seed dirs, etc. */ },
  gracefulShutdownTimeout = 30.seconds
)

Required for languages that flush data on SIGTERM (e.g. Go integration coverage). See Go Container Mode ยท Code Coverage.

Pitfalls

Symptom Fix
Container exits immediately Image entrypoint blocks? Check docker logs <id>
Readiness probe times out App listens on 0.0.0.0, not 127.0.0.1; check port binding
Env var ignored Confirm the AUT reads that exact variable name
Tests can't reach app Mixing host network mode with port binding; pick one

Pairs well with