Deploying marimo with Docker

Deploying marimo with Docker

marimo is an open-source reactive notebook for Python that’s reproducible, git-friendly (stored as Python files), executable as a script, and — the focus of this blog post — deployable as an app.

The marimo CLI lets you run a marimo notebook as a read-only app with

marimo run app.py

which you can test locally.

Of course, not every notebook needs to be run as an app; marimo is excellent for reproducible, interactive, and tangible experimentation, computation, and data exploration. But for notebooks that you do want to share as apps, deployment is straightforward and can be facilitated with standard tools like Docker.

Docker Terminology

  • A Dockerfile is a text file that contains a set of instructions for building a Docker image. It specifies the base image, the dependencies, and the commands needed to set up the environment for running the application.
  • An Image is a self-contained package that includes everything needed to run an application, including the code, runtime, libraries, and system tools. It is created based on the instructions in the Dockerfile and can be shared and deployed to different environments.
  • Layers: In a Dockerfile, each instruction line creates a layer in the Docker image. These layers are stacked on top of each other, with each layer representing a change to the file system. Layers are used for caching and optimizing the build process.
  • A Container is a running instance of an image. It is an isolated and lightweight environment that contains all the dependencies and configurations needed to run the application. Containers can be started, stopped, and managed independently, making them easy to deploy and scale.

The basics

Let’s assume in our marimo application, we only have 2 files: a notebook file called app.py, and a requirements file requirements.txt listing the packages used by the notebook.

Our Dockerfile might look something like this:

# syntax=docker/dockerfile:1.4
 
# Choose a python version that you know works with your application
FROM python:3.11-slim
 
WORKDIR /app
 
COPY --link requirements.txt .
# Install the requirements
RUN pip install -r requirements.txt
 
# You may copy more files like csv, images, data
COPY --link app.py .
# COPY . .
 
EXPOSE 8080
 
# Create a non-root user and switch to it
RUN useradd -m app_user
USER app_user
 
CMD [ "marimo", "run", "app.py", "-p", "8080" ]

Breaking down each step

FROM instructs what base image to choose. In our case, we chose Python 3.9 with the “slim” variant. This removes a lot of extra dependencies. You can always add them back as needed.

A slimmer Dockerfile (by bytes) means quick to build, deploy, and start up.

The WORKDIR sets the current working directory. In most cases, this does not need to be changed.

The COPY steps will copy all the necessary files into your docker. By adding --link, we end up creating a new layer that does not get invalidated by previous changes. This can be especially important for expensive install steps that do not depend on each other.

RUN lets us run shell commands. We can use this to install dependencies via apt-get, pip, or package managers. In our case, we use it to install our requirements.txt with pip.

Our EXPOSE step tells us which port is exposed to be accessed from outside the Docker container. This will need to match the port at which we run our marimo application on.

We then create a new user and switch to it with the USER instruction, in order to limit the permissions of the marimo application. This is not required, but recommended.

The final step CMD instructions what command to run when we run our docker container. Here we run our marimo application at the port 8080.

Going further

We didn’t cover all the Docker features that you may want to use with marimo. For example, you may want to take a look at ENV or Build secrets.

Running your application locally

Once you have your Dockerfile and your application files, you can test it out locally.

# Build your image, and tag it as my_app
docker build -t my_app .
 
# Start your container, and map our port 4000 to our containers 8080
docker run -p 4000:8080 -it my_app
 
# Visit http://localhost:4000

After you have verified your application runs without errors, you can use these files to deploy your application on your favorite cloud provider that supports deploying dockererized applications.

The marimo community

We’re building a community around marimo and the future of Python notebooks. Come hang out with us!

P.S. marimo notebooks can also be run entirely in the browser using WebAssembly (WASM) — no Docker containers required! WASM notebooks are the easiest way to share simple marimo notebooks and apps. Visit https://marimo.new for a sneak peek, and stay tuned for more info!