Tutorial: Learn Gatling from Scratch (2026)
When I needed to load test a real-time trading platform, JMeter could not generate enough throughput without distributed setup. Gatling changed the game with its asynchronous architecture and expressive Scala DSL. It handles thousands of concurrent users from a single machine with minimal resource consumption.
In this tutorial, you will learn how to write Gatling simulations using the Scala DSL, set up realistic load profiles, analyze the rich HTML reports, and integrate load testing into your CI pipeline. Gatling's code-as-test approach makes performance tests maintainable and version-controllable.
Setting Up Gatling and Your First Simulation
Gatling runs on the JVM and requires Java 8 or later. Download the bundle from gatling.io or use the Maven plugin for project integration. Gatling's simulation scripts are written in Scala, but you do not need deep Scala knowledge — the DSL is intuitive.
A simulation defines the scenario — a sequence of HTTP requests — and the load model — how many virtual users execute the scenario. Run Gatling with ./gatling.sh and select your simulation. The HTML report opens automatically after the test completes.
import io.gatling.core.Predef._
import io.gatling.http.Predef._
class BasicSimulation extends Simulation {
val httpProtocol = http.baseUrl("https://api.example.com")
val scn = scenario("Basic Load Test")
.exec(http("health_check").get("/health"))
setUp(scn.inject(atOnceUsers(10))).protocols(httpProtocol)
}
Modeling User Behavior with Scenarios
Scenarios model real user journeys — login, browse, search, add to cart, checkout. Use exec blocks to chain HTTP requests. Gatling supports pauses (think time), loops, conditional execution, and random data via feeders.
I use the exec() method with string interpolation to build dynamic requests. Pauses between requests simulate human think time with random distributions. The scenario DSL reads like a user story, making it easy for non-technical stakeholders to review the load model.
val scn = scenario("User Journey")
.exec(http("login").post("/auth/login")
.formParam("email", "user@example.com")
.formParam("password", "secret"))
.pause(2, 5) // think time 2-5 seconds
.exec(http("search").get("/search?q=shoes"))
.pause(1)
.exec(http("checkout").post("/orders"))
Load Profiles and Injection Patterns
Gatling provides several injection patterns to model different load scenarios. Use rampUsers to gradually increase load, constantUsersPerSec for steady load, and nothingFor for pauses between phases. You can compose multiple injection steps in a single simulation.
For realistic tests, combine ramp-up, steady-state, and spike patterns. The throttle() method limits the global request rate. I always include a warm-up phase of low load before the main test to allow JIT compilation and connection pooling to stabilize.
setUp(
scn.inject(
nothingFor(10.seconds),
rampUsers(100).during(30.seconds),
constantUsersPerSec(50).during(60.seconds),
rampUsers(0).during(10.seconds)
)
).protocols(httpProtocol)
Feeders for Dynamic Data
Feeders provide dynamic input data to your simulations. Use CSV, JSON, or JDBC feeders to load real data sets. Each virtual user picks the next record sequentially or randomly. Feeders are essential for realistic load tests — you want different users with different data.
I use CSV feeders with realistic user credentials, product IDs, and search terms. The circular strategy recycles data when the file ends. For random data, use the random strategy. Gatling also supports custom feeders that generate data programmatically.
val feeder = csv("users.csv").circular
val scn = scenario("Data-Driven")
.feed(feeder)
.exec(http("login").post("/auth/login")
.formParam("email", "${email}")
.formParam("password", "${password}"))
Response Validation and Assertions
Gatling's check method lets you validate HTTP responses within the simulation. Check status codes, JSON paths, or regex patterns. Global assertions compare aggregate metrics — like 95th percentile response time — against thresholds, causing the simulation to fail if exceeded.
I always add status checks to each request and global assertions for response time and success rate. Simulations that fail assertions produce non-zero exit codes, which fail your CI pipeline. This gates releases based on performance criteria.
exec(http("get_user")
.get("/users/${userId}")
.check(status.is(200))
.check(jsonPath("$.name").is("Alice")))
setUp(scn.inject(rampUsers(100).during(30)))
.assertions(global.responseTime.percentile(95).lt(2000))
.assertions(global.successfulRequests.percent.gt(99))
Gatling Reports and CI Integration
Gatling generates a comprehensive HTML report with graphs for active users, response time distribution, requests per second, and error rates. The report includes percentiles, standard deviation, and a simulation log. It is self-contained and shareable.
For CI, use the Maven or Gradle plugin to run simulations as part of the build. Gatling also supports Jenkins, TeamCity, and Bamboo plugins. The gatling-maven-plugin runs simulations during the integration-test phase and fails the build if global assertions do not pass.
mvn gatling:test -Dgatling.simulationClass=com.example.BasicSimulation
# Report generated at target/gatling/basicsimulation-{timestamp}/index.html
Frequently Asked Questions
What is the difference between Gatling and JMeter?
Gatling uses an asynchronous, non-blocking architecture that handles more concurrent users per machine. JMeter is thread-based and requires more resources. Gatling simulations are code (Scala) rather than XML, making them version-control friendly.
Do I need to know Scala to use Gatling?
Basic familiarity with Scala syntax is helpful, but Gatling's DSL is simple enough that Java developers can pick it up quickly. The structure reads like plain English with method chaining.
Can Gatling test WebSocket or gRPC?
Gatling supports WebSocket through the gatling-websocket module. For gRPC, use the gatling-grpc community plugin. HTTP/2 is natively supported.
How do I run Gatling in headless mode?
Use the batch mode: ./gatling.sh -s com.example.MySimulation -rf results/. In CI, use the Maven or Gradle plugin with no interactive prompts.
Originally published on Ayodhyyya. Last updated June 1, 2026.