Deploying a Roundhouse app with Kamal

14 Oct 2026
Security For Rails Developers

Develop the right mindset for Rails security

Avoid shipping vulnerable code by learning how to prevent security issues in your Rails applications.

Get the course for $99

During the latest episode of The Pingback Podcast, we were talking about Roundhouse and Raul asked me how would you deploy a Rails app, compiled to a binary with Roundhouse and Spinel. It is actually pretty simple with Kamal, and here comes a little post about how to do it.

But first of all, let me explain what Roundhouse and Spinel is. Spinel is an ahead of time compiler to create a binary from Ruby code. The resulting code runs much faster than using CRuby, but Spinel has some limitations, it doesn’t support the whole standard library or gems and even many Ruby features is not supported yet. This is where Spinel comes to the picture. It can transpile Ruby code, including Rails apps(not any Rails app as of yet, but there is a good momentum behind the project and I think it will be a viable solution in the future to improve the performance of a Rails app should you need), to a few other languages and it can also compile a Rails app with Spinal, by handling the parts not supported in Spinel yet.

Now that you know what Roundhouse and Spinel are, let’s move on to deploying. Roundhouse uses the Campfire app as an example, so we will use that for deployment. I am not going to detail how to compile it, you can check that in the Roundhouse docs and you can even add the pre-compilation as a before deploy hook with Kamal, but I want to keep this example simple, so we just do the deployment.

Once you have the compiled Campfire app, you should have a bin/build/campfire executable binary. On top of this, you will need a Dockerfile like this:

# syntax=docker/dockerfile:1

FROM debian:trixie-slim AS build

RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y \
      clang \
      make \
      libsqlite3-dev \
      libjemalloc-dev \
      libvips-dev && \
    rm -rf /var/lib/apt/lists/*

COPY pack/ /src/
RUN make -C /src -j"$(nproc)" CC=clang

FROM debian:trixie-slim AS runtime

RUN apt-get update -qq && \
    apt-get install --no-install-recommends -y \
      ca-certificates \
      ffmpeg \
      libjemalloc2 \
      libsqlite3-0 \
      libvips42 \
      util-linux && \
    rm -rf /var/lib/apt/lists/* && \
    groupadd --system --gid 1000 campfire && \
    useradd --uid 1000 --gid 1000 --no-create-home \
      --shell /usr/sbin/nologin campfire

WORKDIR /app

COPY static/ ./static/
COPY public/ ./public/
COPY db/ ./db/
COPY config/ ./config/
COPY --from=build /src/campfire ./campfire
COPY --chmod=755 docker-entrypoint.sh ./docker-entrypoint.sh

RUN mkdir -p storage && chown campfire:campfire storage

VOLUME ["/app/storage"]
ENV PORT=3000 \
    RAILS_ENV=production \
    BLOG_DB=/app/storage/production.sqlite3
EXPOSE 3000

ENTRYPOINT ["./docker-entrypoint.sh"]
CMD []

We use a Debian image as a base, install the necessary packages like sqlite, and copy over the files we need in the container. We also need a docker-entrypoint.sh file with this content:

#!/bin/sh
set -eu

# Named Docker volumes are initially root-owned. Repair ownership before
# dropping privileges; subsequent boots retain the existing database/uploads.
if [ "$(id -u)" = "0" ]; then
  chown -R campfire:campfire /app/storage
  exec setpriv --reuid=campfire --regid=campfire --init-groups \
    ./campfire "$@"
fi

exec ./campfire "$@"

This will execute the binary and that start a process on port 3000 so you now just need to setup Kamal and se3t the app_port to 3000 and you are ready to deploy a Rails app, compiled to a binary by Roundhouse and Spinel!

Or follow me on Twitter

Related posts