Cloud-native batch
|
This section documents the current Spring Batch line — 6.0.x, on Spring Framework 7 and Spring Boot 4.1.x, with a Java 17+ baseline — as published at the Spring Batch reference documentation. No specific patch version is pinned. Some surfaces (Spring Cloud Task and Spring Cloud Data Flow orchestration, the deployer-based partition handler, and JSR-352) are linked, not documented in depth. This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. |
A batch job in a container is still a batch job — but the environment changes some assumptions: the filesystem is ephemeral, the process may be evicted, configuration arrives from outside, and something else decides when to start it.
The twelve-factor lens
| Factor | What it means for a batch job |
|---|---|
Config in the environment |
Connection strings, input and output locations and tuning knobs come from environment variables or a config server — never from a properties file baked into the image. Business inputs stay |
Processes are stateless |
Local disk vanishes when the pod does. Stage files in object storage, and keep the only durable state in the |
Backing services are attachable |
The metadata database is a resource like any other; point at it by URL, and never assume it is the same instance as the business database. |
Disposability |
The pod can be evicted at any moment. Small chunks, restartable steps, and |
Logs as event streams |
Write to stdout in a structured format; the platform collects it. Do not write a log file next to the output. |
Admin processes |
A batch job is an admin process — it should be launchable as a one-off run of the same image, with different parameters. |
Externalized configuration
Spring Cloud Config supplies environment-specific settings, so one image runs in every environment:
spring:
application:
name: end-of-day-batch
config:
import: "optional:configserver:http://config-server:8888"
batch:
jdbc:
initialize-schema: never # the schema is managed by migrations, not the app
job:
enabled: true
Keep the distinction sharp: configuration (where the database is, how big a chunk is) belongs in properties;
business inputs (which date, which file) belong in JobParameters, because they define which
JobInstance this is.
Guarding remote calls
A step that calls a remote service inherits that service’s failure modes. Combine the framework’s retry (Fault tolerance: skip & retry) with a circuit breaker so a sustained outage fails fast instead of retrying a million times:
@Component
public class PricingItemProcessor implements ItemProcessor<Trade, PricedTrade> {
private final CircuitBreaker circuitBreaker;
private final PricingClient pricingClient;
public PricingItemProcessor(CircuitBreakerFactory<?, ?> factory, PricingClient pricingClient) {
this.circuitBreaker = factory.create("pricing");
this.pricingClient = pricingClient;
}
@Override
public PricedTrade process(Trade trade) {
BigDecimal price = circuitBreaker.run(
() -> pricingClient.price(trade.getIsin()),
throwable -> trade.getLastKnownPrice()); // fallback: degrade, do not fail
return new PricedTrade(trade, price);
}
}
Whether the fallback is acceptable is a business decision — for a valuation run, a stale price may be worse than a failed job.
Spring Cloud Task
Spring Cloud Task is the companion project for short-lived applications. It records that a task ran — start
time, end time, exit code, exit message — in its own TASK_EXECUTION tables, and links those records to the
Spring Batch executions the task launched.
@SpringBootApplication
@EnableTask
public class EndOfDayTaskApplication {
public static void main(String[] args) {
SpringApplication.run(EndOfDayTaskApplication.class, args);
}
}
The division of labour: Spring Batch knows about steps, chunks and restart; Spring Cloud Task knows about process lifecycle and exit codes, which is what an orchestrator actually schedules. A batch application that is launched as a container per run is naturally both.
This is linked, not documented in depth here — see Spring Cloud Task.
Spring Cloud Data Flow
Spring Cloud Data Flow orchestrates such tasks: registering an application, launching it with parameters, scheduling it, viewing execution history, and — through the composed task runner — running a DAG of tasks with conditional transitions, the same idea as a job flow but one level up, between applications.
# register the image, then launch it with job parameters
dataflow:> app register --name end-of-day --type task --uri docker://example/end-of-day-batch:1.4.0
dataflow:> task create end-of-day-task --definition "end-of-day"
dataflow:> task launch end-of-day-task --arguments "--spring.batch.job.name=endOfDayJob" \
--properties "app.end-of-day.run.date=2026-01-31"
Also linked, not documented in depth — see Spring Cloud Data Flow documentation.
Kubernetes
A batch application maps directly onto a Kubernetes Job, and a scheduled one onto a CronJob — which is
the platform playing the scheduler role Spring Batch deliberately does not
(What Spring Batch is & running a first job):
apiVersion: batch/v1
kind: CronJob
metadata:
name: end-of-day
spec:
schedule: "30 2 * * 1-5"
concurrencyPolicy: Forbid # never two runs of the same job at once
successfulJobsHistoryLimit: 7
jobTemplate:
spec:
backoffLimit: 0 # let Spring Batch decide about retries, not the platform
template:
spec:
restartPolicy: Never
terminationGracePeriodSeconds: 120
containers:
- name: batch
image: example/end-of-day-batch:1.4.0
args:
- "--spring.batch.job.name=endOfDayJob"
env:
- name: SPRING_DATASOURCE_URL
valueFrom:
secretKeyRef: { name: batch-db, key: url }
Four details matter in practice:
-
concurrencyPolicy: Forbidprevents an overrunning job from being launched twice — Spring Batch would refuse the duplicate instance anyway, but failing at the platform level is clearer; -
backoffLimit: 0stops Kubernetes from blindly restarting a failed pod; a restart should be a deliberateJobOperator.restart(…)so that completed steps are skipped; -
terminationGracePeriodSecondsmust exceed one chunk, so aSIGTERMcan stop the job cleanly rather than killing it mid-transaction; -
the exit code should reflect the outcome (
SpringApplication.exit(…)), so the platform can distinguish success from failure.
The DeployerPartitionHandler (from Spring Cloud Task) extends partitioning to this world: instead of
threads or long-running workers, each partition is launched as its own pod, which then exits. It suits
bursty workloads on elastic infrastructure, and it is linked, not documented in depth here — the local and
message-based partition handlers are covered in
Scaling & parallel processing.