# phusion/passenger-docker: base images for Ruby, Python, Node.js and Meteor apps

> Passenger-docker is a set of Docker base images that bundle Ubuntu, an init process, Nginx and Phusion Passenger, so your Dockerfile only has to describe the app. It suits teams that want a preassembled Ruby, Python, Node.js or Meteor runtime and can accept the image's opinions about service supervision and Ruby version management.

**phusion/passenger-docker** — Docker base images for Ruby, Python, Node.js and Meteor web apps

- Repository: https://github.com/phusion/passenger-docker
- Stars: 2,816 · Forks: 438
- Language: Shell
- License: MIT
- Published: 2026-09-28 · Updated: 2026-09-28 · Language: en
- Canonical page: https://hysenlabs.com/projects/phusion-passenger-docker

## What passenger-docker is for

Writing a correct Dockerfile for a web app involves more than copying source code. You need an init process that reaps zombies, a syslog daemon, cron, a service supervisor, a web server, an application server and a build toolchain with development headers for native extensions. Each of those is a place to get the base system wrong, and the project's stated reason for existing is that it "sets up the base system correctly" so you can focus on your app.

The audience is narrow but specific. If you deploy Ruby, Python, Node.js or Meteor applications and you want a starting image that already has Ubuntu 26.04 LTS, Nginx 1.24 and Phusion Passenger 6 in place, this is aimed at you. If your application is a static site, a Go binary, or something that does not need an application server, the image carries components you will never start.

## What sits inside the image

The image builds on baseimage-docker, which supplies the Ubuntu base, a correct init process, APT fixes for Docker, syslog-ng, the cron daemon and runit for service supervision. On top of that, passenger-docker adds language runtimes and the web tier.

Ruby support covers 3.3.12, 3.4.10, 4.0.7, plus JRuby 10.0.2.0 and 9.4.14.0, managed through RVM, with 4.0.7 as the default. Python 3.14 is included, and the README says any version from the Deadsnakes PPA is available, currently 3.10 through 3.13. Node.js 24 is the default, with 20 and 22 available through Nodesource. JRuby runs on OpenJDK 17 for the 9.4 line and OpenJDK 21 for 10.0.

Nginx 1.24 and Phusion Passenger 6 are both disabled by default, because Passenger starts along with Nginx. Redis 7.0 and Memcached are listed as auxiliary services and are not installed by default. The README claims a default memory footprint of about 10 MB, describing that as the memory consumed by bash, runit, syslog-ng and similar processes; that figure is the project's own statement, not an independent measurement.

## Image variants and how to pick one

Passenger-docker is not a single image. The README lists Ruby images such as phusion/passenger-ruby33, phusion/passenger-ruby34 and phusion/passenger-ruby40, plus phusion/passenger-jruby94 and phusion/passenger-jruby100. The Makefile shows the same naming pattern extended to Python and Node.js, with variables listing ruby33, ruby34, ruby40, python310 through python314, jruby94, jruby100 and nodejs, alongside two special images named customizable and full.

That naming scheme is the main decision you make. Choosing ruby34 pins you to Ruby 3.4 at image build time, and choosing python314 pins you to Python 3.14. If you need a version outside the published set, the README points at the Deadsnakes PPA for Python and Nodesource for Node.js, which means you are back to installing a runtime yourself on top of the base.

## Using the image as a base for a first container

There is no installer. The images are published to a registry, and the README links to the Docker registry page for phusion/passenger-full. Pulling an image is the whole setup step for the base, and the README's own registry link is the documented place to get it.

The README documents how to run a one-shot command in a new container, and how to run a command in an existing, running container, along with logging in via docker exec. Those sections are where the exact invocation for your situation lives, because the flags depend on whether you want a throwaway container or a shell inside one that is already up.

For a real app you write a Dockerfile that uses one of these images as its base, copies your application in, and enables the web tier. The README documents that Nginx and Passenger are disabled by default, so a container started without enabling them will not serve HTTP. Startup scripts placed in the documented location run during container startup, which is where enabling services belongs.

## The RVM decision and what it costs you

Ruby versions are managed with RVM rather than rbenv or chruby, and the README addresses this directly in its FAQ. That choice shapes how you invoke Ruby. The README describes selecting a default Ruby version, running a command with a specific Ruby version, and default wrapper scripts, which means the image is designed around switching between installed Rubies rather than around one system Ruby.

If your team already standardizes on rbenv, this image will feel foreign, and the wrapper scripts are the part most likely to surprise you in CI. The image also carries multiple Ruby versions at once, which is convenient for testing across versions and wasteful if you only ever run one.

## Administration, SSH and the insecure key

The README documents several ways to get into a running container: a one-shot command in a new container, a command in an existing running container, docker exec, and SSH. SSH is not on by default. Enabling it involves a key story that the README treats at length, including an insecure key that can be enabled for one container only or permanently, and instructions for supplying your own key. There is also a docker-ssh tool mentioned by name.

The insecure key is the part to read carefully before enabling SSH in anything reachable. The documentation gives you the mechanism; it does not decide your exposure for you. For most deployments, docker exec covers the same ground without opening a port, and the README presents it as a first-class option rather than a fallback.

## Where it is the wrong tool

The image is opinionated about the operating system. Ubuntu 26.04 LTS is the base, and the README documents upgrading the operating system inside the container as a supported operation, which tells you the project expects long-lived images that drift from the base. If your organization standardizes on Alpine or a distroless base for size or CVE surface reasons, this image is the opposite of that approach: it is a full Ubuntu system with syslog-ng, cron and runit running.

Nginx and Passenger being disabled by default is a second trap. A Dockerfile that copies an app in and expects HTTP on the first run will get nothing, because the web tier has to be enabled explicitly. Teams coming from images that start a server automatically will spend their first debugging session on this.

Finally, the project is a base image, not a deployment system. It does not orchestrate containers, manage secrets, or handle rollbacks. The README does not document rollback, and nothing in the repository layout suggests a release management layer. If you need those, they come from elsewhere.

## Building the images yourself

The repository ships a Makefile, and its defaults are worth knowing before you run it. NAME defaults to $(REGISTRY)/phusion/passenger with REGISTRY set to docker.io, or ghcr.io when GITHUB_ACTIONS is true. VERSION defaults to 3.2.0. Both can be overridden, and the Makefile comments give an example of pointing NAME at an ECR repository and building a single image for amd64 only.

```bash
NAME="YOURID.dkr.ecr.us-west-2.amazonaws.com/passenger" VERSION=2.5.1.4 BUILD_ARM64=0 make -j1 build_ruby34 push_ruby34
```

According to the comment above that example, this builds and pushes YOURID.dkr.ecr.us-west-2.amazonaws.com/passenger-ruby34:2.5.1.4-amd64 and a latest-amd64 tag. Architecture selection is controlled by BUILD_AMD64 and BUILD_ARM64, both defaulting to on, and EXTRA_BUILD_FLAGS passes additional flags to docker build, with --no-cache given as the example. The Makefile comment warns that when adding a cRuby image you must also update image/nginx-passenger.sh and image/ruby_support/finalize.sh, which is the kind of coupling that makes local forks diverge from upstream over time.

## Licence, maintenance and upgrade cost

The repository is MIT licensed, with LICENSE.txt at the top level. MIT is permissive and places few obligations on how you redistribute the image, but the image bundles Ubuntu, Nginx, Passenger, RVM, Redis, Memcached and language runtimes, each under its own terms. The README does not enumerate those licences, so if you redistribute the image commercially, the bundled components are the ones to check. This is not legal advice.

On maintenance: the repository is not archived, and the last push was on 2026-09-15. That is recent enough that the project is being touched, but the material retrieved contains no release notes, so there is no published changelog to read for upgrade guidance beyond CHANGELOG.md in the repository root.

The upgrade cost is structural. Because the image pins language versions and an Ubuntu release, moving forward means moving the base image tag, and the README's own sections on upgrading the OS inside the container and upgrading Passenger to the latest version exist precisely because those two layers age independently. If you build your own images from the Makefile, you also own the nginx-passenger.sh and finalize.sh coupling described above.

## Conclusion

Adopt passenger-docker if you want a preassembled Ubuntu base with Nginx and Phusion Passenger already wired together and your Dockerfile only needs to add the app. Do not adopt it if you want a minimal image, a non-Ubuntu base, or a version manager other than RVM. Before committing, verify which image variant matches your runtime (ruby34, python314, nodejs, jruby94), confirm that Nginx being disabled by default fits your startup scripts, and read the Makefile's NAME and VERSION defaults if you plan to build the image yourself rather than pull it.

## FAQ

### What is phusion/passenger-docker?

It is a set of Docker base images intended as starting points for Ruby, Python, Node.js and Meteor web app images. The images bundle Ubuntu 26.04 LTS, baseimage-docker's init and service supervision, Nginx 1.24 and Phusion Passenger 6.

### Which passenger-docker image should I use for Ruby or Python?

The README lists Ruby images named phusion/passenger-ruby33, phusion/passenger-ruby34 and phusion/passenger-ruby40, plus phusion/passenger-jruby94 and phusion/passenger-jruby100. The Makefile also lists python310 through python314 and nodejs among the buildable images, so the variant name tells you which runtime version the image carries.

### Why is Nginx not serving HTTP in my passenger-docker container?

Both Nginx and Phusion Passenger are disabled by default in the image, and Passenger starts along with Nginx. A container started without enabling them will not serve HTTP, so enabling the web tier belongs in the container startup scripts the README describes.

### Does passenger-docker use rbenv or chruby for Ruby versions?

No. RVM is used to manage Ruby versions, and the README's FAQ has a section titled "Why are you using RVM? Why not rbenv or chruby?" that addresses the choice. Ruby 4.0.7 is configured as the default version.

## Sources

- [Issues](https://github.com/phusion/passenger-docker/issues)
- [License: MIT](https://github.com/phusion/passenger-docker/blob/master/LICENSE)
- [phusion/passenger-docker on GitHub](https://github.com/phusion/passenger-docker)
- [README](https://github.com/phusion/passenger-docker/blob/master/README.md)

---

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