# ngx_mruby: running Ruby logic inside nginx through mruby

> ngx_mruby embeds the mruby scripting language into nginx so that server-side logic can be written in Ruby, with a Docker image for a first run and a documented three-branch release plan. The catch is that the default branch is a moving target and only the latest 2.x release carries support.

**matsumotory/ngx_mruby** — ngx_mruby - A Fast and Memory-Efficient Web Server Extension Mechanism Using Scripting Language mruby for nginx

- Repository: https://github.com/matsumotory/ngx_mruby
- Website: https://ngx.mruby.org/
- Stars: 997 · Forks: 114
- Language: C
- License: not declared
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/matsumotory-ngx-mruby

## Ruby in the request path, without a second application server

ngx_mruby is an nginx extension mechanism built on the mruby scripting language. The problem it addresses is narrow and real. Nginx is very good at proxying, caching and serving static files, but if you want conditional logic in the request path you normally reach for a sidecar application, a language module such as Lua, or a rewrite rule that grows until it is unreadable.

This project takes the option of writing that logic in Ruby. The implementation language is C, and the repository layout shows where the work lives: a src directory for the nginx module code, an mrbgems directory for mruby gem extensions, and an mruby directory for the interpreter itself. There is also a dependence directory, a configure script alongside configure.in and Makefile.in, and a build.sh that drives the whole thing.

The intended users are nginx operators who want scripting power without deploying a separate runtime, and people who prefer Ruby syntax to the alternatives. The project describes itself as fast and memory-efficient, and it is an embed rather than a bridge, so there is no network hop between nginx and the script.

That last point is the whole appeal and also the whole risk. Ruby running inside the server process means a script can affect the server itself, which is exactly the point of the trust boundary the project documents.

## A Ruby API with phases, and what changes in v3

The Ruby side of the module is not a free-form scripting box, it is an API tied to the phases of request processing. The v3 plan lists what that means in practice: a Ruby API called outside the phase it is designed for raises an exception, an exception after `rputs` returns a 500, `Headers#delete` matches the full header name, `Nginx.return 200` with an empty body is allowed, and a body is sent with any status.

Those five sentences are the most useful part of the documentation for an adopter, because they describe the edges where a script can behave in a way you did not predict. A phase violation raising an exception means a misplaced call is loud rather than silent, and the rule about a body with any status tells you the module will not refuse to send one.

The example directory shows the intended shape of usage. It contains an nginx.conf and a set of Ruby files, including proxy.rb, set.rb, set2.rb and unified_hello.rb, which suggests the common patterns of proxying, setting variables and serving a unified response.

Note that those example files are shipped as examples rather than as a documented API contract, and the class and method documentation is a separate page in the docs directory. Read both before assuming a method behaves the way you expect.

## A Docker image gets you a running server in two commands

The quickest first use is the published image. The Dockerfile in the repository documents the pull and run steps, and the image exposes the standard web ports and starts nginx as its command.

```bash
docker pull matsumotory/ngx-mruby
docker run -d -p 10080:80 matsumotory/ngx-mruby
```

The port mapping publishes the container's port 80 on host port 10080, and the documented way to check that the extension is alive is a request to the mruby-hello path:

```bash
curl http://127.0.0.1:10080/mruby-hello
```

If you would rather build the image yourself, the same file documents the manual build path:

```bash
docker build -t your_name:ngx_mruby .
docker run -d -p 10080:80 your_name:ngx_mruby
```

The build inside the image clones the repository and compiles it, so the Dockerfile is also the best available description of the build dependencies. It installs git, curl, rake, ruby and ruby-dev, bison, make, gcc, and the development headers for OpenSSL, cURL, hiredis, markdown2, cap, cgroup, PCRE and MySQL. The nginx configure options are passed through an environment variable that includes the SSL and stub status modules and a prefix of /usr/local/nginx.

One caveat to weigh before relying on the image. The base is ubuntu:18.04, a distribution release that is old enough to be a security consideration for anything exposed to the internet. Treat the image as a development and evaluation convenience.

## Pin the v2.x branch, because a release tag receives no fixes

The support model is unusual enough that reading it is part of adopting the project. The default branch is master, and master is the 2.x line until v3 is promoted to it, so that anyone building from the default branch keeps receiving the 2.x security fixes. Three long-lived branches exist, master, next and v2.x, and the gh-pages branch holds the site.

The rule for production is stated directly. Build from `v2.x` or from the latest v2.* release tag:

```bash
git clone -b v2.x https://github.com/matsumotory/ngx_mruby.git
git clone -b vX.Y.Z https://github.com/matsumotory/ngx_mruby.git
```

The second line is a placeholder for the latest 2.x release tag. The reason to prefer the branch over the tag is that a release tag does not receive fixes. If you pin a tag you must move to each new 2.x release yourself, because only the latest 2.x release is supported according to the supported versions policy. Pinning the `v2.x` branch is the lower-maintenance option, since a workflow copies every push to master onto it.

Until the promotion of v3, v2.x has the same commits as master, so pinning it changes nothing today. That equivalence ends when v3.0.0 is tagged, at which point master becomes the 3.x line and v2.x carries stability and security fixes for the 2.x line for twelve more months.

## v3 raises the floor to mruby 4.1, nginx 1.30 and OpenSSL 3.5

The v3 plan fixes the support baseline, and the numbers matter for anyone still on an older distribution package.

The interpreter changes from mruby 3.3 to mruby 4.1. The oldest supported nginx versions become 1.30 stable and 1.31 mainline, and that floor is raised every April, which means users of older nginx, including 1.28 and the 1.26 and 1.24 packages shipped by Linux distributions, stay on the 2.x line. The OpenSSL baseline moves to 3.5, with 1.1.1 and 3.0 dropped, and auto-ssl is removed from the default build.

So a host running an older distribution nginx or an old OpenSSL is not stuck with a broken project. It is scheduled to stay on 2.x until it upgrades, and the plan says the v3 migration guide will list each behaviour change individually.

The promotion itself has no date set. v3 is developed on the next branch with pre-releases tagged v3.0.0-alpha.N, v3.0.0-beta.N and v3.0.0-rc.N, and the conditions for each are written down: alpha.1 requires the 2.x test suite to pass on the new core and the sanitizers to report nothing, beta.1 requires the migration guide and examples to exist, rc.1 requires the site and examples to be complete. At least one release candidate comes before v3.0.0, and how long the last one stays out is still open.

## Your configuration is trusted, your clients are not

The project publishes a trust boundary statement, which is unusual and worth reading before you write a single line of Ruby for it. The configuration and its Ruby code are trusted. What a client sends is not. And the document states what each side is responsible for.

That framing determines how the module should be used. Script code lives in the same trust domain as the nginx configuration, so it belongs in the same review process as a rewrite rule or a certificate path, with the same scrutiny on who can change it and how it reaches a production host. Client input is the untrusted half, and the responsibility split says who has to sanitise it.

This matters more here than in most nginx modules because a mistake in a script does not just produce a wrong page. Script code runs inside the server, so an unbounded loop, an allocation mistake or an unhandled error affects the worker that is serving traffic.

The related documentation set covers the same ground in pieces, with pages for install, test, directives, classes and methods, use cases and release notes, and a note that the release notes carry behaviour changes to read before upgrading.

## A busy release cadence, and a licence that needs reading directly

On maintenance, the record is healthy. The last push was on 2026-10-19, and v2.7.1 was released the same day, following v2.7.0 on 2024-11-07 and v2.6.0 on 2024-05-21. The 2.x line is documented as receiving stability fixes for crashes, hangs and leaks, build fixes for new nginx, OS, compiler or OpenSSL releases, and security fixes, while changing behaviour only as far as fixing a defect requires.

The upgrade cost for a 2.x user is therefore a rebuild from the v2.x branch or a move to the newest 2.x release tag, plus reading the release notes in docs/releases, which from the next release on list each such change. There is no migration work to do, and there is no in-place upgrade of a running nginx, since this is a module and a new binary is involved.

On the licence, the information is thin. The licence is not identified by a standard identifier, and the repository root contains a file named MITL along with a file named LEGAL. That pairing suggests the module code and the bundled dependencies carry separate terms, which is normal for a project that vendors a copy of mruby and pulls in other libraries. If the licence terms matter to your use, read MITL and the files in LEGAL, and treat the absence of a recognised identifier as a question to answer rather than a detail to assume.

The tree also carries a SECURITY.md with the supported versions policy, which is the document to consult for how long a given line stays supported.

## Conclusion

Adopt ngx_mruby if you need Ruby in the request path of an existing nginx deployment and you can accept owning an nginx module build, since the Docker route gets you a running server quickly. Do not adopt it if you need a stable long-lived release, since only the latest 2.x release is supported and a pinned tag receives no fixes. Verify three things first: that you can build from the `v2.x` branch and rebuild on each new 2.x release, which OpenSSL and nginx versions your host already has, and whether the MITL and LEGAL files in the repository root grant the terms you need, since the licence is not identified by a standard identifier.

## FAQ

### Which ngx_mruby branch should I build from for production?

Build from the `v2.x` branch or from the latest v2.* release tag. The default branch `master` is the 2.x line until v3 is promoted to it, and a pinned release tag receives no fixes, so you would have to move to each new 2.x release yourself.

### How do I run ngx_mruby with Docker?

Pull the published image and publish the container port 80 on a host port, for example `docker run -d -p 10080:80 matsumotory/ngx-mruby`. The documented check is a request to `http://127.0.0.1:10080/mruby-hello`.

### What does the ngx_mruby trust boundary mean in practice?

The configuration and the Ruby code it loads are trusted, while anything a client sends is not, and the documentation states what each side is responsible for. Script code therefore belongs in the same review process as the nginx configuration itself.

### When will ngx_mruby 3.0.0 be released?

No date is set. v3 is developed on the `next` branch with alpha, beta and release candidate pre-releases, and v3.0.0 is tagged when `next` is promoted to `master` under the conditions described in the development documentation.

### What versions of nginx, mruby and OpenSSL does ngx_mruby v3 require?

The plan sets mruby 4.1 instead of 3.3, nginx 1.30 stable and 1.31 mainline as the oldest supported versions with that floor raised every April, and OpenSSL 3.5 as the baseline with 1.1.1 and 3.0 dropped. Users of older nginx stay on the 2.x line.

## Sources

- [Issues](https://github.com/matsumotory/ngx_mruby/issues)
- [matsumotory/ngx_mruby on GitHub](https://github.com/matsumotory/ngx_mruby)
- [Project website](https://ngx.mruby.org/)
- [README](https://github.com/matsumotory/ngx_mruby/blob/master/README.md)
- [Releases](https://github.com/matsumotory/ngx_mruby/releases)

---

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