Cloud EngineeringAWS BatchRasterio

Scaling Geospatial Pipelines: From 10 Files to 10,000 in AWS Batch

The production lessons behind moving an imagery workflow from local scripts to an observable, permission-safe, highly parallel cloud pipeline.

Infryne TechWorks9 min read
Geospatial imagery tiles branching into thousands of parallel cloud processing nodes before becoming processed map layers
A single imagery manifest fans out into independently scheduled processing jobs, then converges into validated outputs.
tiles per run
10 → 10,000
memory per job
4–8 GB
array-job ceiling
10,000
case-study quota target
1,000 vCPUs

Scaling a geospatial pipeline is rarely only a coding problem. It is an infrastructure problem involving memory, identity, concurrency, observability, and cost. This is the behind-the-scenes architecture we used to make all five behave as one production system.

01. Treat “Essential container exited” as a starting point

Our first AWS Batch runs failed with the unhelpfully broad Essential container in task exited message. The useful signal lived one layer deeper in the exit code and container reason.

Exit code 137

The kernel killed the process

Raster operations can expand compressed imagery into large in-memory arrays. If the job reaches its memory ceiling, the operating system can kill it immediately. Profile representative tiles, then raise the job definition from 2 GB to 4 or 8 GB only when the measurements justify it.

Exit code 1

The application crashed

In our case Python failed before buffered logs reached CloudWatch. The job looked silent even though the process had already found a missing file and exited.

We made Python logs stream immediately from the container:

# Dockerfile
ENV PYTHONUNBUFFERED=1

# Equivalent runtime option
CMD ["python", "-u", "main.py"]
Operational lesson: always log the tile key, array index, memory configuration, and processing phase before opening the raster. A crash should identify the unit of work without requiring a rerun.

02. Give the platform and the workload separate identities

A headless container does not inherit your laptop credentials. Our NoCredentialsError disappeared once we separated the permissions required to launch a task from the permissions required by the code inside it.

Execution role

Runs the container

  • Pulls the image from Amazon ECR
  • Writes container logs to CloudWatch Logs
  • Retrieves referenced secrets when configured

Job role

Runs the application

  • Reads only the required S3 input prefixes
  • Writes only the designated output prefixes
  • Uses least-privilege access for any other AWS API

AWS documents the same separation: the execution role is for the ECS/Fargate agent, while the task or job role is for application calls to services such as S3. Review the AWS IAM role guide.

03. Use one array job to coordinate 10,000 independent tiles

Once a single tile completed reliably, the workload became naturally parallel: every child job could use its own AWS_BATCH_JOB_ARRAY_INDEX to select one record from a validated manifest.

aws batch submit-job \
  --job-name imagery-production \
  --job-queue geospatial-prod \
  --job-definition imagery-worker:12 \
  --array-properties size=10000

AWS Batch supports array sizes from 2 to 10,000. The parent job becomes the management handle while each index is scheduled as a separate child job. That is ideal for tile processing because failures and retries stay isolated to individual inputs. See the official array-job behavior.

Workflow diagram showing S3 imagery passing through an AWS Batch array job into 10,000 indexed containers and processed outputs
The array index is the contract between the manifest and each child container; outputs remain deterministic and independently retryable.

04. Scale compute deliberately—not all at once

Submitting 10,000 jobs does not mean 10,000 should start simultaneously. In our environment, the initial 32-vCPU service quota limited throughput, so we requested a case-specific increase to 1,000 vCPUs and kept the compute environment bounded by cost and downstream capacity.

We also smoothed the first S3 request from every container with bounded jitter and SDK retries. Jitter is not a replacement for proper prefix design or exponential backoff; it simply avoids an artificial burst caused by thousands of identical startup paths.

import os
import random
import time

index = int(os.environ["AWS_BATCH_JOB_ARRAY_INDEX"])
time.sleep(random.uniform(0, 180))  # smooth the startup wave
process_tile(manifest[index])

Quota

Set maximum vCPUs to a measured throughput and budget target.

Storage

Distribute hot request paths and keep compute in the same Region as S3.

Retries

Use SDK retry behavior with exponential backoff and monitor 503 responses.

Amazon S3 scales to high request rates, but scaling is gradual and brief 503 Slow Down responses can occur during a sharp ramp. Read the current S3 performance guidance.

05. Make geospatial functions explicit and testable

Infrastructure stability exposed the next failure: a TypeError around Rasterio's rasterize() call. We removed positional ambiguity by switching to explicit keyword arguments and making the output geometry part of the log context.

from rasterio.features import rasterize

mask = rasterize(
    shapes=features,
    out_shape=(height, width),
    transform=transform,
    fill=0,
    default_value=1,
    dtype="uint8",
)

Keyword arguments do not become safer only because concurrency is high; they make the call contract clearer across library upgrades and easier to diagnose when thousands of jobs execute the same code path.

06. The final production workflow

StepActionOutcome
DebugForced Python logs to stdoutFound the missing file and fixed the Docker build context.
PermissionsAssigned execution and job rolesResolved S3 authentication with explicit workload identity.
LogicMade rasterize arguments explicitStabilized the processing function and its diagnostics.
ScaleRaised the bounded compute quotaEnabled high concurrency without unlimited spend.
OperateAdded metrics, retries, and per-tile logsMade failures observable and independently recoverable.

Start small

Prove ten tiles before you submit ten thousand.

Run a representative smoke test, make IAM permissions explicit, confirm memory headroom, verify deterministic output keys, and watch billing metrics from day one. Scale only after the system explains its own failures.

Planning a larger run?

Design the failure path before the scale test.

We help teams turn fragile geospatial scripts into observable cloud pipelines with clear resource, security, and cost boundaries.

Further reading