Sitemap
iyzico.engineering

The engineering behind iyzico, the sweet home for iyzicoders…

Run JMeter Load Tests on AWS with Docker & Compose

5 min readJan 26, 2026

--

Package once, run anywhere — EC2 execution, S3 report uploads, zero JMX internals.

Table of Contents

  1. Why Docker + AWS for JMeter?
  2. What We’ll Build (Architecture)
  3. Prerequisites
  4. Project Layout
  5. Dockerfile (containerizing JMeter)
  6. Docker Build
  7. docker-compose.yml (one-command runs)
  8. Local Dry-Run (optional)
  9. Create Task Definition
  10. Create Cluster and Run Task
  11. Result S3

Why Docker + AWS for JMeter?

  • Reproducible: same image, same runtime, fewer “works on my machine” issues.
  • Portable: local dev, EC2, or CI — identical command.
  • Automatable: compose brings one-line runs; S3 stores artifacts cleanly.

**We don’t cover the JMeter script (.jmx) itself. Assume you already have a valid plan.

In our QA infrastructure at iyzico, running JMeter directly on EC2 or local machines used to create inconsistency — plugin mismatches, Java versions, and path issues. Containerizing the environment solved all of these, and we could finally reproduce load runs identically across local, staging, and production-like setups.

What We’ll Build (Architecture)

  • A single Docker image that contains JMeter CLI + (optional) plugins.
  • A compose service that runs the plan headlessly, generates HTML report, and uploads artifacts to S3.
  • An EC2 instance (Amazon Linux) with Docker/Compose installed; optional IAM role grants S3 PutObject.

Flow:
Local Environment(optional build) → EC2 (compose up) → JMeter runs → JTL + HTML report → ZIP → S3

Get Nurullah şahin’s stories in your inbox

Join Medium for free to get updates from this writer.

This approach was driven by a need to simplify how we trigger performance tests in CI/CD. Instead of maintaining multiple AMIs or EC2 setup scripts, everything now lives in a single image definition. When a new feature needs stress testing, the team can launch a task definition directly — no manual setup, no SSH.

Prerequisites

  • An AWS account and an S3 bucket (e.g., my-loadtests).
  • I AM permissions:
    EC2 path: an Instance Profile (role) with s3:PutObject for your bucket/prefix.
  • ECS path: a Task Role with s3:PutObject; an Execution Role for ECR pull & logs.
  • (ECS only) An ECR repository to store your image; a VPC with subnets & a security group.
  • Local Docker and docker compose (for build & optional dry-run)

Project Layout

jmeter-aws/
Dockerfile
docker-compose.yml
jmeter/
test.jmx # your existing JMeter test plan (left as-is)
user.properties # optional JMeter overrides
data.csv # optional data file
results/ # mounted by compose (JTL, logs, HTML here)

Dockerfile (containerizing JMeter)

A minimal yet practical image (includes AWS CLI and popular plugins; remove plugins if you don’t need them).

# Dockerfile
FROM adoptopenjdk/openjdk11:x86_64-alpine-jre-11.0.18_10

USER root

# (Optional) Plugins Manager + cmdrunner, then install a couple of common plugins
RUN apk update && \
apk add --update --no-cache curl py-pip && \
apk add ca-certificates && \
update-ca-certificates && \
pip install --upgrade --user awscli

# Set variables
ENV JMETER_HOME=/usr/share/apache-jmeter \
JMETER_VERSION=5.6.2 \
TEST_SCRIPT_FILE=test.jmx \
TEST_RESULT_DIR=/var/jmeter \
TEST_LOG_FILE=/var/jmeter/test.log \
TEST_RESULTS_FILE=/var/jmeter/test-result.csv \
TEST_REPORT_DIR=/var/jmeter/result/html-report \
THREAD_GROUP_NAME="thread name" \
VARIABLE="value"

# Install Apache JMeter
RUN wget http://archive.apache.org/dist/jmeter/binaries/apache-jmeter-${JMETER_VERSION}.tgz && \
tar zxvf apache-jmeter-${JMETER_VERSION}.tgz && \
rm -f apache-jmeter-${JMETER_VERSION}.tgz && \
mv apache-jmeter-${JMETER_VERSION} ${JMETER_HOME}

ENV PATH="$JMETER_HOME/bin:$PATH"

# Copy jmeter files
ADD jmeter/script /var/jmeter/

WORKDIR /loadtest
COPY plan/ ./plan/
COPY run.sh ./run.sh
RUN chmod +x ./run.sh

# - Start the JMeter script
CMD echo -n > $TEST_LOG_FILE && \
echo -n > $TEST_RESULTS_FILE && \
export PATH=~/.local/bin:$PATH && \
. ./usr/local/bin/set_submerchant_detail.sh && \
$JMETER_HOME/bin/jmeter -n \
-t=/var/jmeter/$TEST_SCRIPT_FILE \
-j=$TEST_LOG_FILE \
-l=$TEST_RESULTS_FILE \
-JthreadGroupName=$THREAD_GROUP_NAME \
-JrandomVariableForTest=$VARIABLE && \
echo -e "\n\n===== UPLOADING THE FILES TO AWS S3 =====\n\n" && \
aws s3 cp $TEST_LOG_FILE s3://performance-test-logging/uploads/$THREAD_GROUP_NAME/ && \
aws s3 cp $TEST_RESULTS_FILE s3://performance-test-logging/uploads/$THREAD_GROUP_NAME/ && \
aws s3 cp $TEST_REPORT_DIR s3://performance-test-logging/uploads/$THREAD_GROUP_NAME/ --recursive && \
echo -e "\n\n===== UPLOAD COMPLETED SUCCESSFULLY =====\n\n"

Docker Build

Build locally; optionally tag for ECR:

# local build
docker build -t jmeter-aws:latest .

# optional multi-arch (if you need both amd64/arm64)
# docker buildx create --use
# docker buildx build --platform linux/amd64,linux/arm64 -t <account>.dkr.ecr.<region>.amazonaws.com/jmeter-aws:latest --push .

# tag for ECR (single-arch path)
aws ecr get-login-password --region <region> | \
docker login --username AWS --password-stdin <account>.dkr.ecr.<region>.amazonaws.com
docker tag jmeter-aws:latest <account>.dkr.ecr.<region>.amazonaws.com/jmeter-aws:latest
docker push <account>.dkr.ecr.<region>.amazonaws.com/jmeter-aws:latest

Docker-compose.yml (one-command runs)

Environment variables let you tweak runs without touching the JMX.

# docker-compose.yml
version: "3.8"

services:
jmeter:
build: .
image: docker-image:latest
platform: linux/amd64
environment:
AWS_REGION: "eu-central-1"
S3_BUCKET: "my-loadtests" # leave empty to skip upload
S3_PREFIX: "runs/${COMPOSE_PROJECT_NAME:-local}"
THREAD_GROUP_NAME: "thread_group_name"
VARIABLE: "value"
volumes:
- ./results:/loadtest/results

Local Dry-Run (optional)

docker-compose -f <docker-compose_filePath> up <service_name>

#docker-compose -f docker-compose.yml up jmeter

Create Task Definition

1- ECR: push your image (see DockerBuild).

2- I am Roles:

  • Task Role: allow s3:PutObject to arn:aws:s3:::my-loadtests/*.
  • Execution Role: standard AmazonECSTaskExecutionRolePolicy (pull from ECR, push logs)

3- Task Definition (Linux): example container def (JSON gist):

{
"family": "jmeter-aws",
"networkMode": "awsvpc",
"cpu": "1024",
"memory": "2048",
"executionRoleArn": "arn:aws:iam::<account>:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::<account>:role/jmeterS3TaskRole",
"containerDefinitions": [
{
"name": "jmeter",
"image": "<account>.dkr.ecr.<region>.amazonaws.com/jmeter-aws:latest",
"essential": true,
"environment": [
{ "name": "AWS_REGION", "value": "<region>" },
{ "name": "S3_BUCKET", "value": "my-loadtests" },
{ "name": "S3_PREFIX", "value": "runs/ecs" },
{ "name": "THREAD_GROUP_NAME", "value": "threadh group name" },
....
{ "name": "EXTRA_JMETER_ARGS", "value": "-Jthreads=200 -Jduration=180 -Jjmeter.reportgenerator.overall_granularity=1000" }
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/jmeter-aws",
"awslogs-region": "<region>",
"awslogs-stream-prefix": "jmeter"
}
}
}
]
}

Create Cluster and Run Task

  1. Create an ECS cluster
  2. Run Task (one-off):
  • VPC/Subnets: public (assign public IP) or private + NAT
  • Security group: egress allowed
  • Task overrides (optional): tweak EXTRA_JMETER_ARGS for this run

3. Watch CloudWatch Logs stream for progress. The container exist when finished.

Result S3

Verify upload:

aws s3 ls s3://my-loadtests/runs/ecs/
# report-YYYYMMDD-HHMMSS.zip

Download & inspect:

aws s3 cp s3://my-loadtests/runs/ecs/report-YYYYMMDD-HHMMSS.zip .
unzip report-YYYYMMDD-HHMMSS.zip
# open report/index.html in your browser

That’s it. You now have a script-agnostic, containerized way to run JMeter locally, on EC2, or serverlessly via ECS — always shipping clean artifacts to S3.

In real practice, this setup has helped us reduce setup time by more than 70%. Every test run produces a clean HTML report in S3 with timestamped folders, which makes trend comparison effortless.

If you’re dealing with multiple environments or distributed teams, this containerized JMeter pattern is one of the most maintainable ways to keep load testing consistent and auditable.

Press enter or click to view image in full size

--

--

Nurullah şahin
Nurullah şahin
iyzico.engineering
iyzico.engineering