# Jetty: an embeddable Java web server for HTTP/1, HTTP/2, HTTP/3 and Servlets

> Jetty is an Eclipse Foundation Java web server and Servlet engine that can run as a standalone distribution or be embedded directly in an application. This article covers how the start.jar module system works, how to deploy a WAR, and where Jetty is the wrong choice.

**jetty/jetty.project** — Eclipse Jetty® - Web Container & Clients - supports HTTP/3, HTTP/2, HTTP/1, websocket, servlets, and more

- Repository: https://github.com/jetty/jetty.project
- Website: https://jetty.org/
- Stars: 4,096 · Forks: 2,021
- Language: Java
- License: NOASSERTION
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/jetty-jetty-project

## What Jetty is and which Java teams it fits

Jetty is a Java web server and Servlet engine maintained under the Eclipse Foundation. The README describes it as "a lightweight, highly scalable, Java-based web server and Servlet engine" whose goal is to support web protocols including HTTP/1, HTTP/2, HTTP/3 and WebSocket in a high volume, low latency way, while keeping compatibility with Servlet development.

The distinguishing claim is that Jetty is "a modern fully asynchronous web server" with a component-oriented history, so it can be embedded into applications and still ship as a traditional distribution for webapp deployment. That dual identity drives most of the design decisions below.

The intended audience splits into two groups the project names itself. The Operations Guide targets sysops, devops and developers who want Jetty as a standalone server for deploying web applications. The Programming Guide targets developers who want to use Jetty libraries inside their own applications. If you are writing a Java service and need an HTTP layer you control at the code level, you are in the second group. If you have a WAR file and need somewhere to put it, you are in the first.

Jetty is not a full application server in the sense of bundling an enterprise stack you enable once and forget. The enabled feature set is assembled from modules, and that assembly is the thing you have to understand before anything else.

## The start.jar module model and how a Jetty base is assembled

A Jetty installation separates the distribution (JETTY_HOME, the read-only product) from the configuration directory (a jetty-base you create). Nothing is enabled until you ask for it. The start.jar launcher reads the modules you have added and builds the classpath and configuration for that run.

That is the central mechanism. Modules such as http and ee11-deploy are not present in a fresh base until --add-modules writes them there. Once added, they persist in the base directory, so a later start.jar invocation with no arguments starts the same configuration. This is why the README's examples create a directory first and then add modules into it rather than passing everything on one command line.

The repository layout reflects the same structure. Top-level directories include jetty-core, jetty-ee8, jetty-ee9, jetty-ee10, jetty-ee11, jetty-home, jetty-integrations and jetty-demos. The ee* trees correspond to different Jakarta EE and Servlet API generations, which is how one Jetty release can host webapps written against several Servlet versions at once.

One consequence worth stating plainly: the module list is your deployment manifest. If a capability is missing at runtime, the first thing to check is whether the module that provides it was ever added to the base, not whether the code is wrong.

## Installing Jetty and deploying a first WAR

The README gives the standalone workflow directly. You need a JETTY_HOME pointing at the unpacked distribution, then you create a base directory, add the modules you want, drop a WAR into webapps, and start the server. The first command creates the base and enables plain HTTP plus the Jakarta EE 11 deploy module.

```bash
mkdir jetty-base && cd jetty-base
java -jar $JETTY_HOME/start.jar --add-modules=http,ee11-deploy
```

After this runs, the base directory contains the module configuration for http and ee11-deploy. The next two commands copy your built webapp into the webapps directory and start Jetty with the base you just configured.

```bash
cp ~/src/myproj/target/mywebapp.war webapps
java -jar $JETTY_HOME/start.jar
```

The README does not state a default port for this configuration, so check the generated configuration in the base rather than assuming one.

If you need to host webapps built against two Servlet generations at the same time, the README shows adding both deploy modules and tagging each WAR with a properties file. The echo line writes a one-line properties file next to the WAR, and the deploy module picks it up.

```bash
java -jar $JETTY_HOME/start.jar --add-modules=http,ee11-deploy,ee8-deploy
cp ~/src/myproj/target/mywebapp10.war webapps
cp ~/src/myproj/target/mywebapp8.war webapps
echo "environment: ee8" > webapps/mywebapp8.properties
```

For the embedded case the README shows the minimal server: construct a Server with a port, set a handler, start. A Servlet variant wraps a ServletContextHandler at the root path and registers the servlet with a /* mapping.

```java
Server server = new Server(port);
ServletContextHandler context = new ServletContextHandler("/");
context.addServlet(MyServlet.class, "/*");
server.setHandler(context);
server.start();
```

To build Jetty itself from source, the README gives a clone followed by a Maven build with the fast profile, which it says bypasses tests and other checks.

```bash
git clone https://github.com/jetty/jetty.project.git
cd jetty.project
mvn -Pfast clean install
```

## Where the module model and the licence metadata get in the way

The module approach has a real cost. A base directory is stateful, and the state lives in files rather than in a build script unless you put it there. Two machines with the same Jetty version can behave differently because one had an extra module added months ago. The README's examples are honest about this, but they do not describe a rollback or a way to diff a base against a known-good one. If you need reproducible deployments, you have to treat the base directory as a build artefact yourself, and the documentation does not hand you a mechanism for that.

The multi-version webapp example also carries a constraint that is easy to miss. Tagging a WAR with an environment properties file only works when the corresponding ee*-deploy module for that environment is already added to the base. Add ee8-deploy and forget ee11-deploy, and the ee11 webapp has nowhere to go.

Licence metadata is the other rough edge. The repository root contains LICENSE and NOTICE.txt, and the GitHub API reports the licence as NOASSERTION, meaning it could not be mapped to a recognised SPDX identifier. That is a metadata outcome, not a statement about the actual terms. Anyone embedding Jetty in a product should read LICENSE and NOTICE.txt directly rather than relying on a scanner's summary. Nothing here is legal advice.

Finally, embedding is not free of lifecycle responsibility. The embedded examples show start() and nothing else. Shutdown, thread pool sizing and graceful stop are not covered by the README, and the Programming Guide is where that detail lives.

## Jetty compared with Tomcat as a Servlet container

The comparison people actually search for is Jetty against Tomcat, and the difference in approach is architectural rather than feature-by-feature.

Tomcat's common deployment model is a container you install and then drop webapps into, with a fairly broad default configuration. Jetty's model, as the README presents it, is a distribution plus an explicitly assembled base, where the enabled modules are declared. Jetty also positions itself first as an embeddable component: the README leads with the webapp example but the Programming Guide exists specifically for developers using the libraries inside their applications, and the embedded examples are four lines long.

That matters when your service is a single Java process that happens to speak HTTP. Embedding Jetty means no separate container process, no WAR packaging step, and no out-of-band configuration directory to keep in sync with the application. The trade-off is that you now own the server lifecycle in your code, including the parts the README does not show.

If your requirement is to host third-party WARs you did not build, and you want the container to be a stable, separately managed thing, the standalone base model is closer to what you want, and the operations guide is the relevant document. If your requirement is an HTTP stack inside your own process, the embedded path is the shorter one.

## Release cadence and what upgrading Jetty costs

Two release lines are moving in parallel. The most recent entries listed for the repository are jetty-12.1.13 and jetty-12.0.39, both dated 2026-09-07, following jetty-12.1.12 on 2026-08-04. The default branch is jetty-12.1.x. The last push to the repository was on 2026-09-23.

The parallel lines mean an upgrade decision has two parts. Staying on 12.0.x keeps you on the maintenance line while 12.1.x takes the newer work. Moving between them is not just a version bump, because the ee8, ee9, ee10 and ee11 module trees are separate and a base directory names the modules it uses. An upgrade that changes which deploy modules exist will surface as a startup failure in the base, not as a compile error in your code.

Upgrade cost therefore concentrates in the base directory and the module list, not in the application. That is a smaller surface than a full container migration, but it is not zero, and the README does not document a migration procedure. The contribution guide is linked for building and contributing, which is a different problem.

Commercial support is offered separately by Webtide, which the README names as the provider of expert advice and production support. That is the route for teams that need a support contract rather than community channels.

## Conclusion

Adopt Jetty when you need a Java HTTP stack you can embed in-process, or a standalone container where the enabled modules are declared explicitly rather than implied by a directory scan. Do not adopt it if you want a single packaged distribution with every Jakarta EE feature switched on by default; the module model assumes you will enumerate what you need. Before committing, verify that the ee8, ee9, ee10 or ee11 deploy module matching your WAR is available in the Jetty version you pin, and check the LICENSE and NOTICE.txt files in the repository root, since the GitHub API reports the licence as NOASSERTION rather than a recognised SPDX identifier.

## FAQ

### What is Jetty used for?

Jetty is a Java web server and Servlet engine used either as a standalone server to deploy web applications or as an embedded library inside an application. The README describes its goal as supporting HTTP/1, HTTP/2, HTTP/3 and WebSocket in a high volume, low latency way.

### What is the current version of Jetty?

The most recent releases listed for the repository are jetty-12.1.13 and jetty-12.0.39, both dated 2026-09-07. The default branch is jetty-12.1.x.

### What is jetty vs tomcat?

The README presents Jetty as a component-oriented, fully asynchronous server that can be embedded into applications while also shipping a traditional distribution for webapp deployment, and it does not compare Jetty with Tomcat.

## Sources

- [Issues](https://github.com/jetty/jetty.project/issues)
- [jetty/jetty.project on GitHub](https://github.com/jetty/jetty.project)
- [Project website](https://jetty.org/)
- [README](https://github.com/jetty/jetty.project/blob/jetty-12.1.x/README.md)
- [Releases](https://github.com/jetty/jetty.project/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/jetty-jetty-project
