Clojure - how to connect to running REPL process remotely
This might be obvious to network specialists but took me a while to find out so documenting it here.
On the remote server, when launching your REPL application instead of just lein repl
force binding to a port:
lein repl :start :port 40000
On your machine, connect to the remote server the normal way (for example via ssh). Then connect to your application this way:
lein repl :connect localhost:40000
That's it!
I just want to sum up the two answers above. It works on my machine:
On the remote machine
lein repl :start :port 40000
On the local machine
# SSH tunnel on one shell
ssh -NL 40000:localhost:40000 username@host
# Connect to the remote repl on another shell
lein repl :connect localhost:40000
Well, that's simple. Briefly, there are some steps to be done:
- the
nrepl
package should be a part of the production build, but not just a dev dependency; - When your app starts, it also spawns a repl session in a separate thread on a certain port;
- your server either exposes that port or you tunnel it through SSH.
Now the details:
1) Add these deps into the primary :dependencies
vector:
:dependencies [[org.clojure/clojure "1.9.0"]
;; for remote debugging
[cider/cider-nrepl "0.17.0"]
[org.clojure/tools.nrepl "0.2.13"]
You need cider-nrepl
in case you work with Emacs/Cider. Otherwise, you may omit that.
2) Add a separate namespace to wrap nrepl server:
(ns project.nrepl
(:require [clojure.tools.nrepl.server
:refer (start-server stop-server)]))
(defn nrepl-handler []
(require 'cider.nrepl)
(ns-resolve 'cider.nrepl 'cider-nrepl-handler))
(defonce __server (atom nil))
(def set-server! (partial reset! __server))
(def port 7888)
(defn start
[]
(when-not @__server
(set-server!
(start-server :port port :handler (nrepl-handler)))))
(defn stop
[]
(when-let [server @__server]
(stop-server server)
(set-server! nil)))
(defn init
[]
(start))
In your core module, just call (project.nrepl/init)
. Now your app allows connecting to it through nrepl.
3) On remote server, you may expose the TCP 7888 port to the outer world which is insecure. At least the port should be restricted from certain IP addresses, e.g. your office. The better option would be to forward it through SSH as follows:
ssh -L 7888:<remote-host>:7888 <user>@<remote-host>
Now, open Emacs, call M-x cider-connect RET localhost 7888
and it's done: you are connected to the remote app.