View all services
Talk to QA Advisor
Browse the Knowledge Hub211 resources
/Tools Library/Gatling
Tools Library · Review and guide

Gatling review: pricing, pros and cons

Gatling is an open-source load testing tool in which scenarios, called simulations, are written as code and run on an asynchronous engine built on Netty. Each run produces an HTML report, and assertions fail the build when thresholds are breached. Community Edition is Apache 2.0; Gatling Enterprise Edition is the paid platform for distributed runs. Version 3.16.0 was released on 29 September 2026.

QAble has evaluated this tool and works alongside it. The factual sections are complete; our view is bounded to what we have actually seen rather than a long-term verdict.

last reviewed
13 October 2026
version covered
Gatling 3.16
licence
Open source

Reviewed by Viral Patel

Co-Founder, QAbleLast reviewed 13 October 2026

At a glance

Gatling at a glance

Official site
Vendor
Gatling Corp
Category
Performance testing
Licence
Open source (Apache 2.0, with Gatling Enterprise)
Platforms
JVM
Languages
Java, Kotlin, Scala, JavaScript, TypeScript
Integrates with
Maven, Gradle, sbt, npm, Jenkins, Karate
First released
2012
Version reviewed
Gatling 3.16

Verdict

Our verdict on Gatling

Strengths

  • Virtual users are messages on a non-blocking engine, so one machine drives high concurrency without a thread per user
  • Simulations are code in Java, Kotlin, Scala, JavaScript or TypeScript, so they diff and review like application code
  • A static HTML report with percentiles, throughput and errors per request is generated after every run
  • Assertions on response times and error rates fail the Maven, Gradle or npm command, so CI can gate on them
  • Open and closed injection models are explicit, which forces a deliberate choice about how load arrives
  • Community Edition is Apache 2.0 with no cap on users or duration for HTTP, WebSocket, SSE and JMS

Weaknesses

  • Distributed load generation, live dashboards and run comparison require the paid Enterprise Edition
  • gRPC, MQTT and GraphQL are capped at 5 users and 5 minute tests without Enterprise
  • The split between build time and run time in the DSL confuses newcomers for days
  • Many tutorials still use the bundle and ${} syntax that Gatling 3.11 removed
  • Protocol coverage beyond HTTP is narrower than JMeter, and Kafka or JDBC rely on community plugins

Our take

Gatling fits teams that want load tests to live in the repository as code, reviewed like any other change, and that need high concurrency without a fleet of load generators. The asynchronous engine and the report produced with no extra setup are its clearest advantages over JMeter, and the Java, Kotlin and JavaScript or TypeScript SDKs mean Scala is no longer the entry fee. QAble has evaluated it rather than maintained a long-running suite on it, so treat this as a bounded take. Watch three things. Distributed load generation, live dashboards and run trends sit in the paid Enterprise Edition, as does unrestricted gRPC, MQTT and GraphQL. Tutorials written before 3.11 rely on the removed standalone bundle and ${} expressions, which no longer work. And the JavaScript SDK runs on GraalVM inside a JVM rather than on Node.js, so Node modules such as fs are not available.

We have evaluated this tool rather than maintained a long-running suite on it, so this is a bounded view rather than a long-term verdict. The rest of this page is factual.

How it works

How Gatling works

Virtual users are messages, not threads

Gatling’s engine is built on Netty, a non-blocking network library. A virtual user is not an operating system thread parked on a socket: it is a small piece of state handed from action to action, and a pool of shared threads moves users along as responses arrive. Thread-per-user tools such as JMeter spend memory and context switches on every waiting user; Gatling spends them only on work, which is why it reaches high concurrency on modest hardware. Version 3.12 removed the Akka library that once provided the actor model and replaced it with an executor and a multiple-producer, single-consumer queue as each actor’s mailbox. The design has one rule you must respect: functions you write run on those shared threads, so a blocking call inside one, such as a remote API call, stalls users who have nothing to do with it.

A simulation is a recipe built once

A simulation class declares an HTTP protocol, one or more scenarios, and a setUp call that binds each scenario to an injection profile. The DSL objects are immutable builders: Gatling runs your class once at start-up to assemble the recipe, then executes that recipe for every virtual user. Ordinary Java, Kotlin or Scala in a builder therefore runs once. Anything that must differ per user or per iteration has to be deferred, either as an Expression Language string such as "#{orderId}" or as a function that receives the user’s Session. Only Gatling SDK methods interpret EL strings, so passing one to your own helper does nothing. Most confusing behaviour in a first Gatling project comes from this split between build time and run time.

Session state, feeders and checks

Each virtual user carries a Session, an immutable map of attributes that actions read and replace. Feeders push records into it from CSV, JSON or other sources, consumed with a queue, random, shuffle or circular strategy. Checks run on each response: they verify status codes, headers or body content, and extract values with JMESPath, JsonPath, CSS selectors or regular expressions, which saveAs stores for later requests. That is how correlation works: a token in the login response becomes "#{token}" in the next request. A failed check marks the request KO, and the user carries on unless the scenario uses exitHereIfFailed, exitBlockOnFail or tryMax. Without an explicit status check, Gatling applies an implicit one that accepts 2xx and 304 responses.

Open and closed injection

Every scenario is injected with either injectOpen or injectClosed, and the steps inside one call must all be the same kind. Open steps such as constantUsersPerSec and rampUsersPerSec control how many users arrive per second, regardless of how quickly the system answers, which is how most websites and public APIs behave. Closed steps such as constantConcurrentUsers hold the number of users in the system constant, which matches systems with a hard cap and a queue, such as a call centre or a ticketing site. Gatling’s documentation warns that a closed model applied to an open system is misleading: as response times rise, the injection slows to match the cap you set, so the test quietly applies less load exactly when the system struggles.

From a run to a pass or fail

While a simulation runs, Gatling records each request’s outcome to a simulation.log file. When it ends, it builds a static HTML report from that log and evaluates the assertions declared in setUp. Assertions target global statistics, every request with forAll, or one request or group with details, and test response time metrics, request counts, failure percentages or requests per second. If any assertion fails, the simulation fails, and so does the Maven, Gradle or npm command that launched it. That exit status is what turns a load test into a pipeline gate. Logging is buffered, so a run that is killed forcibly can lose its last data points.

Strengths

What it does well

High concurrency from one load generator

Because a waiting user costs no thread, one machine sustains far more concurrent virtual users than a thread-per-user tool on the same hardware, and Community Edition places no cap on test duration or users for HTTP, WebSocket, Server-Sent Events or JMS. This matters when the generator would otherwise be the bottleneck, or when CI runners are the only machines available for load.

Simulations in five languages

The JVM SDK is written for Java and is used from Kotlin as well; Scala has its own DSL. The JavaScript SDK, in a separate repository, covers JavaScript and TypeScript. All of them drive the same engine and produce the same report, so the choice follows the team’s skills rather than the tool’s limits. Simulations are ordinary source files that diff, review and refactor like the application they test.

Assertions as a release gate

Assertions express service levels in code: a 95th percentile under 500 ms, fewer than 1 per cent failed requests, a throughput range for a named group. They are evaluated after the run, and a breach fails the build. That turns performance from a report someone reads into a check that blocks a merge, which is the habit that catches regressions before release.

An HTML report with no setup

Every run writes a static report under target/gatling: response time ranges, a summary table with percentiles per request, users started per second for open models or concurrent users for closed ones, response time distribution, percentiles over time, and requests and responses per second. A per-request chart plots response time against global throughput. No plugin, database or dashboard server is involved.

The Recorder for a first draft

The Recorder works as an HTTP proxy between a browser and the server, or converts a HAR file exported from browser developer tools, and generates a simulation that mimics the recorded navigation, including pauses. It is launched with ./mvnw gatling:recorder, ./gradlew gatlingRecorder or npx gatling recorder. Treat the output as a draft: recorded values need parameterising and dynamic tokens need correlating before the script means anything under load.

Protocols beyond plain HTTP

HTTP/1.1, HTTP/2 (opt-in with enableHttp2), WebSocket, Server-Sent Events and JMS ship under Apache 2.0. gRPC, MQTT and, since 3.16, GraphQL are separate modules under the Gatling Enterprise Component License, limited to 5 users and 5 minute tests without Enterprise. Kafka, AMQP, SFTP, FTP and JDBC are covered by community plugins that Gatling does not maintain, so check their compatibility with your Gatling version.

Pricing

Gatling pricing and licensing

Gatling Community Edition is free and open source under Apache 2.0, including commercial use, with no account and no cap on users or duration for the protocols in its main repository. Gatling Enterprise Edition is the commercial platform built on the same engine. It is sold in three plans, Basic, Team and Enterprise, and consumption is measured in credits: one credit is a 1-minute test on one load generator. The figures below are list prices read on gatling.io/pricing on 13 October 2026.

  • Licence: Apache 2.0 for Community Edition. The gRPC, MQTT and GraphQL modules use the Gatling Enterprise Component License and are limited to 5 users and 5 minute tests without Enterprise.
  • Basic: €89 a month billed annually (€1,068 a year) or €99 a month billed monthly; up to 60,000 virtual users, 1 hour of testing, 1 load generator and 2 seats, with community support.
  • Team: €356 a month billed annually (€4,272 a year) or €396 a month billed monthly; up to 180,000 virtual users, 5 hours of testing, 3 load generators, 10 seats, distributed testing and professional support.
  • Enterprise: price on request, with custom limits and premium support. Private locations, which run generators in your own AWS, Azure, GCP or Kubernetes accounts, are listed as an add-on.
  • How cost scales: by testing minutes multiplied by load generators. Exhausting the included credits does not block the account, but further testing needs more units bought through sales. A 14-day free trial needs no credit card.
  • Hidden costs: with Community Edition you supply and size the load generator, often a CI runner; with either edition, engineering time for correlation, test data and keeping simulations in step with the application.

Checked against the vendor’s own pages on 13 October 2026. Pricing changes; confirm on the official site before buying.

Getting started

Gatling tutorial: getting started

Setup, step by step

  1. Install a 64-bit OpenJDK LTS release from 11 to 25 and check it with java -version.
  2. Clone the official Maven starter, gatling-maven-plugin-demo-java; Gradle, Kotlin and Scala starters also exist.
  3. Run ./mvnw gatling:test (mvnw.cmd gatling:test on Windows); the Maven wrapper downloads everything else.
  4. If several simulations exist, pick one at the prompt, or name it with -Dgatling.simulationClass.
  5. Open index.html in the newest folder under target/gatling to read the report.
  6. Point baseUrl at a test environment you own before raising the load.

Install

git clone https://github.com/gatling/gatling-maven-plugin-demo-java.git
cd gatling-maven-plugin-demo-java
./mvnw gatling:test
# Windows: mvnw.cmd gatling:test
# Reports: target/gatling/<simulation>-<timestamp>/index.html

A first example

// src/test/java/example/BasicSimulation.java
package example;

import static io.gatling.javaapi.core.CoreDsl.*;
import static io.gatling.javaapi.http.HttpDsl.*;

import io.gatling.javaapi.core.*;
import io.gatling.javaapi.http.*;

public class BasicSimulation extends Simulation {

  // Read once at start-up: ./mvnw gatling:test -Drate=5
  private static final int rate = Integer.getInteger("rate", 2);

  // Gatling's public demo API; use your own environment for real load
  private static final HttpProtocolBuilder httpProtocol = http
      .baseUrl("https://api-ecomm.gatling.io")
      .acceptHeader("application/json");

  private static final ScenarioBuilder scn = scenario("Session")
      .exec(
          http("Get session")
              .get("/session")
              .check(status().is(200)));

  {
    setUp(
        // Open model: new users arrive at a fixed rate, however slow the responses
        scn.injectOpen(constantUsersPerSec(rate).during(60)))
        .protocols(httpProtocol)
        // Evaluated after the run; any breach fails the simulation and the build
        .assertions(
            global().failedRequests().count().lt(1L),
            global().responseTime().percentile(95.0).lt(500));
  }
}

What trips people up first

Following a tutorial written for an older Gatling. Before 3.11, Gatling shipped a standalone bundle with bin/gatling.sh and a user-files folder; that bundle is gone, replaced by a Maven wrapper project, so commands from those guides fail. The same release removed the ${} Expression Language syntax: a string such as "/items/${id}" is no longer interpreted, and in Kotlin or Scala it collides with string interpolation. Use "#{id}". It also dropped project-relative resource paths, so a feeder file in src/test/resources/data/users.csv is referenced as data/users.csv. Check the date on any guide, and prefer the official tutorials and the starter projects.

In practice

Using Gatling in practice

Laying out a simulation project

Keep the starter’s Maven layout: simulations in src/test/java, and data files, gatling.conf and logback-test.xml in src/test/resources. As soon as there is more than one journey, move each into its own class of reusable chains (login, search, checkout) and compose scenarios from them, so a change to the login flow is made once. Write one simulation class per test type, such as smoke, load and soak, and select it in CI with -Dgatling.simulationClass. Read everything that varies between runs, such as the base URL, arrival rate and duration, from system properties with Integer.getInteger or System.getProperty and a sensible default, so the same code runs against a laptop, staging and a pre-production environment without edits. In TypeScript projects, getParameter and getEnvironmentVariable do the same job, with values passed as key=value arguments to npx gatling run.

Feeding data and correlating responses

Realistic load needs distinct users and the values the server hands back. A feeder supplies one record per virtual user; the default queue strategy crashes the run when records run out, so use circular or random for long tests, or provide enough rows. Extract dynamic values with checks and saveAs, and reference them later as EL strings. Values that must differ per user belong in the Session or in EL functions such as randomUuid(), never in plain Java inside the builder. In Java, a check’s is method takes a value or a function; use isEL when the expected value is an EL string.

// src/test/java/example/OrdersSimulation.java (excerpt)
// users.csv has a header row: username,password
private static final FeederBuilder<String> users =
    csv("data/users.csv").circular(); // classpath path, not src/test/resources/...

private static final ChainBuilder login = feed(users)
    .exec(
        http("Login")
            .post("/api/login")
            .body(StringBody("{\"username\":\"#{username}\",\"password\":\"#{password}\"}"))
            .asJson()
            .check(
                status().is(200),
                jmesPath("token").saveAs("token"))) // stored only if the check passes
    .exitHereIfFailed(); // no point browsing without a token

private static final ChainBuilder browse = exec(
        http("List orders")
            .get("/api/orders")
            .header("Authorization", "Bearer #{token}")
            .check(jmesPath("orders[0].id").exists()))
    .pause(1, 3); // think time: 1 to 3 seconds, chosen per user

private static final ScenarioBuilder scn = scenario("Orders").exec(login, browse);

Shaping load with injection profiles

Decide first whether the system is open or closed. A public website or API is open: users arrive whether or not earlier ones have finished, so model arrival rates with injectOpen. Use injectClosed only when the real system caps concurrency, such as a worker pool fed from a queue. Ramp up rather than starting at full rate, so the report shows where response times bend. For a capacity search, incrementUsersPerSec builds a staircase of levels separated by ramps, and the response time against global throughput chart shows the level where latency climbs. Set maxDuration as a safety net, because a closed model only ends when users finish their scenarios. Several scenarios can run in one setUp, each with its own profile, which is how a realistic mix of browsing and buying is expressed.

// inside the simulation class; import java.time.Duration
{
  setUp(
      // Ramp to 50 arrivals per second over 2 minutes, then hold for 10
      browse.injectOpen(
          rampUsersPerSec(1).to(50).during(120),
          constantUsersPerSec(50).during(600)),
      // Capacity search: 10, 20, 30, 40, 50 arrivals per second, 2 minutes each
      checkout.injectOpen(
          incrementUsersPerSec(10.0)
              .times(5)
              .eachLevelLasting(120)
              .separatedByRampsLasting(30)
              .startingFrom(10.0)),
      // Closed model only for a capped system, such as a 20-agent back office
      backOffice.injectClosed(constantConcurrentUsers(20).during(600)))
      .protocols(httpProtocol)
      .maxDuration(Duration.ofMinutes(20));
}

Gating a pipeline in GitHub Actions

Gatling needs nothing beyond Java in CI: check out, set up a JDK, run the Maven goal. GitHub Actions sets CI=true, and the Maven plugin then runs non-interactively and fails if more than one simulation is available, so always name the class. Run load tests on a schedule or on demand against a dedicated environment rather than on every push, since shared environments make results noisy and a load test can disrupt other people’s work. Upload target/gatling whether the job passes or fails: the report is the evidence for a failed assertion. Watch the runner itself, because a saturated generator inflates response times; if CPU is the limit, lower the rate per runner or move to bigger machines before trusting the numbers.

# .github/workflows/load-test.yml
name: load-test
on:
  workflow_dispatch:
  schedule:
    - cron: '0 2 * * 1-5'
jobs:
  gatling:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-java@v6
        with:
          distribution: temurin
          java-version: 21
          cache: maven
      # CI=true makes gatling:test non-interactive, so name the simulation
      - run: >
          ./mvnw --batch-mode gatling:test
          -Dgatling.simulationClass=example.OrdersSimulation
          -DbaseUrl=https://staging.example.com
          -Drate=20
      # A failed assertion fails the step above; keep the report either way
      - uses: actions/upload-artifact@v7
        if: always()
        with:
          name: gatling-report
          path: target/gatling

Writing simulations in TypeScript

The JavaScript SDK mirrors the Java DSL closely, so the concepts above carry over. Projects follow the gatling-js-demo layout: simulations in src as files ending .gatling.ts or .gatling.js, with gatling.conf and data in resources. The CLI, installed as @gatling.io/cli, builds and runs them with npx gatling run --typescript --simulation followed by the file’s base name. Be clear about the runtime: the CLI downloads GraalVM and runs the bundle on its JavaScript engine inside a JVM, not on Node.js, so Node built-in modules such as fs are not available, and npm packages that depend on them will fail. GraphQL support is in the Java SDK only for now. Durations are numbers of seconds or objects with an amount and unit.

// src/orders.gatling.ts
import {
  simulation, scenario, csv, jmesPath, rampUsersPerSec, global, getParameter
} from "@gatling.io/core";
import { http, status } from "@gatling.io/http";

export default simulation((setUp) => {
  // npx gatling run --typescript --simulation orders baseUrl=https://staging.example.com
  const baseUrl = getParameter("baseUrl", "http://localhost:8080");
  // resources/data/search-terms.csv has a header row: term
  const terms = csv("data/search-terms.csv").random();

  const scn = scenario("Orders")
    .feed(terms)
    .exec(
      http("Search")
        .get("/api/search?q=#{term}")
        .check(status().is(200), jmesPath("results[0].id").saveAs("itemId")),
      http("Item").get("/api/items/#{itemId}")
    );

  setUp(scn.injectOpen(rampUsersPerSec(1).to(20).during(120)))
    .protocols(http.baseUrl(baseUrl))
    .assertions(global().responseTime().percentile(95).lt(800));
});

Reading results and keeping runs comparable

Start with the summary table, then the percentiles over time chart: a 99th percentile that climbs while the arrival rate is flat points at saturation, whereas a spike during the ramp often points at cold caches or connection setup. Percentile charts include successful requests only, so read them with the failure counts beside them. Name requests and groups deliberately, because those names are what the report, the forAll and details assertions, and any later comparison key on. Assert on percentiles and error rates rather than the maximum, which a single outlier decides. To compare runs over time with Community Edition you keep and compare the archived reports yourself; trends across runs, live dashboards and service level objectives are Enterprise features. Keep the environment, data volume and generator size the same between runs, or the comparison measures the setup rather than the code.

Pitfalls

Common pitfalls

Expressions copied from old tutorials are not interpreted

Why it happens: Gatling 3.11 removed the ${} Expression Language syntax. In Java the placeholder is now sent literally; in Kotlin and Scala it is string interpolation, evaluated once at build time or rejected by the compiler.

The fix: Replace every ${name} placeholder with #{name}. While upgrading, also apply the other 3.11 renames: onCase for withKey, transform for convert, AllowList and DenyList for WhiteList and BlackList, and hostNameAliases instead of the removed virtualHost.

Every virtual user sends the same "random" value

Why it happens: Plain code in a builder, such as UUID.randomUUID() or a random index concatenated into a URL, runs once while Gatling assembles the scenario, so the value is frozen for the whole run.

The fix: Defer it: use an EL function such as #{randomUuid()}, a feeder, or a function that receives the Session, for example .get(session -> "/items/" + session.getString("id")).

The run crashes partway through with an empty feeder

Why it happens: Feeders default to the queue strategy, which hands out each record once. When more users arrive than there are rows, Gatling stops with an error rather than reusing data.

The fix: Use circular or random for long or high-rate tests, or size the file to the planned number of users. Since 3.15 the eager and batch loading options are gone, so remove them from older code.

A closed model hides the slowdown it should reveal

Why it happens: With constantConcurrentUsers, a new user starts only when another finishes, so the arrival rate falls as response times rise. A website tested this way receives less load exactly when it struggles.

The fix: Model internet-facing systems with injectOpen and arrival rates. Keep injectClosed for systems that genuinely cap concurrency and queue the rest.

gatling:test fails in CI with no simulation chosen

Why it happens: Since Maven plugin 4.8.0 and Gradle plugin 3.11.0, the run is interactive by default. When CI is set to true or batch mode is on, the plugin cannot prompt and fails if more than one simulation exists.

The fix: Pass -Dgatling.simulationClass with the fully qualified class name for Maven, or --simulation for gatlingRun in Gradle. In the JavaScript CLI, pass --simulation with the file’s base name.

Code or plugins break after skipping several minor versions

Why it happens: Gatling’s minor releases carry breaking changes: 3.10 needs Java 11 and recompilation, 3.12 renamed Injector to LoadGenerator and removed Akka and the Graphite integration, and 3.14 moved JMS from javax.jms to jakarta.jms.

The fix: Read each upgrade guide between your version and 3.16 in order, upgrade the build plugin with Gatling, and rename stopInjector to stopLoadGenerator. Replace Graphite metrics with Enterprise or your own exporter.

The first request fails or hangs in a locked-down network

Why it happens: By default the HTTP protocol sends a warm-up request to https://gatling.io before the run, to absorb engine start-up cost. Generators without internet access cannot reach it.

The fix: Point the warm-up at your own service with http.warmUp("https://staging.example.com/health"), or switch it off with http.disableWarmUp().

Compared

Gatling alternatives compared

ToolScript languageLicenceVirtual user modelDistributed loadHosted option
GatlingJava, Kotlin, Scala, JavaScript, TypeScriptApache 2.0; gRPC, MQTT, GraphQL commercialMessages on a non-blocking Netty engineEnterprise Edition onlyGatling Enterprise Edition
Apache JMeterXML plans built in a GUI; GroovyApache 2.0One thread per virtual userBuilt in: controller plus remote serversNone from Apache; third-party services
k6JavaScript, TypeScript via esbuildAGPL-3.0Go engine; each VU has own memoryk6 Operator on KubernetesGrafana Cloud k6
LocustPythonMITgevent; one CPU core per processBuilt in: master and workersAzure Load Testing; Locust Cloud discontinued

FAQ

Frequently asked questions

What are the best Gatling alternatives?

The alternatives most often weighed against Gatling are Apache JMeter, k6 and Locust. The comparison on this page sets them side by side on script language, licence, virtual user model, distributed load and hosted option, and the verdict above says where Gatling is the wrong choice and which of them to pick instead.

Is Gatling free?

Gatling Community Edition is free and open source under Apache 2.0, including commercial use, with no account and no cap on users or test duration for HTTP, WebSocket, Server-Sent Events and JMS. The gRPC, MQTT and GraphQL modules are under a commercial component licence and limited to 5 users and 5 minute tests. Gatling Enterprise Edition, the paid platform for distributed runs, dashboards and trends, has Basic, Team and Enterprise plans, with a 14-day free trial.

Gatling vs JMeter: which should I choose?

Choose Gatling when load tests should be code in the repository and you need high concurrency from few machines: its non-blocking engine avoids JMeter’s thread per user, and its report needs no plugins. Choose JMeter when testers prefer building plans in a GUI, when you need its wider protocol coverage, or when you want free distributed runs, since JMeter’s controller and remote servers are built in while Gatling reserves distribution for Enterprise.

Gatling vs k6: what is the difference?

Both treat load tests as code and can fail a build on thresholds. k6 is a single Go binary with JavaScript or TypeScript scripts, AGPL-3.0, with free distribution through the k6 Operator on Kubernetes and Grafana Cloud k6 as the hosted service. Gatling runs on the JVM, supports Java, Kotlin, Scala, JavaScript and TypeScript under Apache 2.0, and produces a richer static report, but distributed runs need Gatling Enterprise Edition.

Do I need to know Scala to use Gatling?

No. Scala was Gatling’s original language and is still supported, but the Java DSL, also used from Kotlin, has been the main entry point for several years, and the official starters, tutorials and README examples lead with Java. The JavaScript SDK adds JavaScript and TypeScript. Every SDK drives the same engine and produces the same report, so pick the language your team already reviews.

Can Gatling run JavaScript or TypeScript tests on Node.js?

You write and launch them with Node.js tooling, through npm and the @gatling.io/cli package, but they do not execute on Node.js. The CLI downloads GraalVM and runs the simulation on its JavaScript engine inside a JVM. Node built-in modules such as fs are therefore unavailable, and npm packages that depend on them fail. The DSL mirrors the Java one, and GraphQL is not yet available in the JavaScript SDK.

What changed in Gatling 3.16, and is it safe to upgrade?

Gatling 3.16.0 was released on 29 September 2026. It adds a GraphQL module for the Java SDK, per-user mutual TLS from a single PKCS#12 file, shared queues between virtual users, built-in counters, a setInSession step, headers for header-less CSV feeders, the randomString and randomOneOf EL functions, and the HTTP QUERY method. The release notes list additions only, but upgrades from older versions must work through every earlier upgrade guide in turn.

Where did the Gatling bundle and gatling.sh go?

Gatling 3.11 replaced the hand-made standalone bundle with a bundle based on the Maven wrapper, which works offline and uses a standard Maven layout that IDEs import. Instead of bin/gatling.sh you run ./mvnw gatling:test, or mvnw.cmd gatling:test on Windows. To migrate, copy user-files/simulations into src/test/java and user-files/resources into src/test/resources, and reference resource files by classpath path.

Can Gatling run distributed load tests for free?

Not as a supported feature. Community Edition generates load from the machine it runs on, and Gatling’s comparison of editions lists managed generators and private locations in your own AWS, Azure, GCP or Kubernetes accounts as Enterprise Edition features. The feeder shard option is a no-op outside Enterprise. For more load from Community Edition, use a larger generator; for coordinated multi-machine runs with one report, Enterprise is the documented route.

Alternatives

Gatling alternatives worth weighing

Resources

Where to go next

Keep going

Prepare, or get it done

Ready

Picking the wrong tool costs more than picking late.

QAble advises on toolchain decisions and then implements them, with engineers who have run these tools on real delivery. Start with a free QA audit.

Talk to QA Advisor