Skip to content

Running your nREPL server in a Docker container

ΘLΞΉ edited this page Jun 10, 2025 · 10 revisions

Why would I do that

The AI can be pretty unpredictable about what it does via the MCP tools, and that's why many AI assistants would ask for a user confirmation before using tools with potentially harmful side effects. If you don't want to always review the commands or if you simply want a better safety by default, it is wise to limit those side effects more or less to only the intended cases, and that's why running the MCP server and the nREPL in an isolated environment makes sense.

Sandboxing with Docker

ClojureMCP is designed so that you only need to sandbox the nREPL process.

The following configuration should make this a fairly straightforward option for you.

Connecting to a Dockerized nREPL with ClojureMCP

The Goal

The objective is to create a generic, reusable Docker image for Clojure development. This guide will help you build a container that can be launched from any Clojure project directory. The container runs a properly configured nREPL server, allowing you to connect from development tools like ClojureMCP, VS Code (with Calva), or Emacs (with CIDER) for seamless interactive development.

1. Project Prerequisite: deps.edn

For any Clojure project you wish to use this setup with, ensure its deps.edn file contains the necessary nREPL dependencies and a server startup alias.

Example deps.edn:

{:paths ["src"]
 :aliases
 {:nrepl
  {:extra-deps {nrepl/nrepl {:mvn/version "1.1.1"}
                cider/cider-nrepl {:mvn/version "0.49.1"}}
   :extra-paths ["test"]
   :main-opts ["-m" "nrepl.cmdline"
               ;; CIDER middleware is recommended for a better experience
               ;; with many development tools.
               "--middleware" "[cider.nrepl/cider-middleware]"
               "--port" "7888"
               ;; This bind address is required for Docker
               "--bind" "0.0.0.0"]}}}

Please pay attention to the --bind 0.0.0.0 as this is very IMPORTANT for this to work.

2. The Generic Dockerfile

Create a file named Dockerfile (with no extension) in a dedicated directory. This file defines our generic Clojure environment. You only need this one file to build the image.

Dockerfile Contents:

# Use the official Clojure tools-deps image
FROM clojure:temurin-21-tools-deps

# The command to run when the container starts.
# It will be executed in the working directory provided by the `docker run -w` flag.
CMD ["clojure", "-M:nrepl"]

3. Building and Running

Follow these two steps to get your nREPL server running.

Step 1: Build the Docker Image (Only Once)

Navigate to the directory where you saved your Dockerfile and run the build command. You only need to do this once to create the reusable image.

docker build -t clojuremcp-repl .

We've tagged (-t) the image as clojuremcp-repl for clarity.

Step 2: Run the Container in Your Project Directory

Now, cd into any of your Clojure projects (that has the required deps.edn setup) and run the following command:

docker run --rm -it -p 7888:7888 -v "$PWD:$PWD" -w "$PWD" clojuremcp-repl

TIP: You can make this a bash script

Your nREPL server is now running. From your host machine, you can connect ClojureMCP to the nREPL at localhost:7888.

Advanced: Mounting Additional Directories

You can easily make other host directories available inside your container by adding more -v (or --volume) flags to the docker run command. This is useful for linking projects or caching dependencies.

Example 1: Linking Another Clojure Project

Imagine your local file structure has two related projects:

/path/to/your/
├── current-project/   <-- You are here ($PWD)
└── other-clj-project/ <-- You want to access this

You can mount other-clj-project into a specific path inside the container, like /other-clj-project. Note that the relative path ../ is resolved on your host machine.

# Using backslashes for readability
docker run --rm -it \
  -p 7888:7888 \
  -v "$PWD:$PWD" \
  -w "$PWD" \
  -v "$PWD/../other-clj-project:$PWD/../other-clj-project" \
  clojuremcp-repl

Now, from within the REPL, you can access files from the second project at the path /other-clj-project.

Example 2: Caching Dependencies (Recommended)

To avoid re-downloading project dependencies every time you start the container, you can mount your local Maven cache (~/.m2) into it.

\# Using backslashes for readability
docker run --rm -it \
  -p 7888:7888 \
  -v "$PWD:$PWD" \
  -w "$PWD" \
  -v "~/.m2:~/.m2" \
  clojuremcp-repl

This command mirrors your host's ~/.m2 directory inside the container, which is where the default user looks for the cache.

You can combine these flags to mount as many directories as you need for your workflow.

Sandbox achieved!

No changes to the claude_desktop_config.json are needed with this setup.

Now you have a single container that you can reuse to run a sandboxed nREPL in many clojure projects.

You can create a little sandbox.sh bash script in the root directory of your projects to help you mirror the needed folders.

You will need to consult the Chat model of your choice to help you set up more complicated containers.

Clone this wiki locally