How-To

Serve a REPL from a remote server

  1. Run an ssh server on your.example.host and perform the usual ssh setup. (For example, add your public key to ~/.ssh/authorized_keys on the server.)
  2. Your Julia server should call serve_repl(). Use @async serve_repl() if you'd like to run other server tasks concurrently.

Connect to a REPL on a remote server

  1. Set up passwordless ssh to your server. Usually this means you have ssh-agent running with your private key loaded. If you've got some particular ssh options needed for the server, you'll find it convenient to set these up in the OpenSSH config file (~/.ssh/config on unix). For example,
    Host your.example.host
       User ubuntu
       IdentityFile ~/.ssh/some_identity
  2. Start up Julia and run the code
    using RemoteREPL; connect_repl("your.example.host");
    Alternatively use the shell wrapper script RemoteREPL/bin/julia-r:
    julia-r your.example.host

Plot variables from the server

The @remote macro can be used to get variables from the server and plot them on the client with a single line of code. For example:

julia@your.host> x = 1:42; y = x.^2;

julia> plot(@remote((x,y))...)

Use stdout with println/dump, etc

Lots of functions such as print write to the global stdout variable, but RemoteREPL doesn't capture this.

There's two ways to get a similar effect in RemoteREPL, both of which rely on passing an IO object explicitly to println/dump/etc.

One way is to use @remote(stdout) which creates a proxy of the client's stdout stream on the server which you can write to:

julia@localhost> dump(@remote(stdout), :(a + b))
Expr
  head: Symbol call
  args: Array{Any}((3,))
    1: Symbol +
    2: Symbol a
    3: Symbol b

Another way is to use sprint to create the IO object and wrap the returned value in a Text object for display:

julia@localhost> Text(sprint(dump, :(a+b)))
Expr
  head: Symbol call
  args: Array{Any}((3,))
    1: Symbol +
    2: Symbol a
    3: Symbol b

Include a remote file

To include a Julia source file from the client into the current module on the remote side, use the %include REPL magic:

julia@localhost> %include some/file.jl

%include has tab completion for local paths on the client.

Evaluate commands in another module

If your server process has state in another module, you can tell RemoteREPL to evaluate all commands in that module. For example:

julia@localhost> module SomeMod
                    a_variable = 1
                 end
Main.SomeMod

julia@localhost> a_variable
ERROR: UndefVarError: a_variable not defined
[...]

julia@localhost> %module SomeMod
Evaluating commands in module Main.SomeMod

julia@localhost> a_variable
1

Use common session among different clients

A session, as implemented in ServerSideSession, describes the display properties and the module under which commands are evaluated. Multiple clients can share same such properties by using the same session_id. For example:

julia> session_id = UUID("f03aec15-3e14-4d58-bcfa-82f8d33c9f9a")

julia> connect_repl(; session_id=session_id)

Pass a command non-interactively

To programmatically pass a command to the remote julia kernel use remotecmd. For example:

julia> con2server = connect_remote(Sockets.localhost, 9093) # connect to port 9093 in localhost

julia> remotecmd(con2server, "myvar = 1") # define a new var

Use alternatives to SSH

AWS Session Manager

You can use AWS Session Manager instead of SSH to connect to remote hosts. To do this, first setup Session Manager for the EC2 instances you like. See the docs. Thereafter, install AWS CLI version 2 and then install the Session Manager plugin for AWS CLI on your local system.

Setup your AWS CLI by running aws configure on the command line. You can then connect to the RemoteREPL server on your EC2 instance with connect_repl("your-instance-id"; tunnel=:aws, region="your-instance-region"). The region argument is only required if the EC2 instance is not in the default region that your CLI was setup with.

Kubernetes kubectl

If kubectl is configured on your local system, you can use that to connect to RemoteREPL servers on your Kubernetes cluster. Run the following snippet: connect_repl("your-pod-name"; tunnel=:k8s, namespace="your-namespace"). The namespace argument is only required if the Pod is not in the default Kubernetes namespace.

Use in Jupyter or Pluto

In environments without any REPL integrations like Jupyter or Pluto notebooks you can use

connect_remote();

which will allow you to use @remote without the REPL mode.

More on Pluto

Pluto presents a peculiarity as the default module is constantly changing. In order to closely track the newest notebook state, you will need to tap into the client's session and update the module. You could write the following code in the pluto notebook that updates the module every second (if you have a better event-driven update solution, please raise an issue!).

using PlutoLinks

using RemoteREPL, Sockets, UUIDs

server = Sockets.listen(Sockets.localhost, 27765)

@async serve_repl(server)

session_id = UUID("f03aec15-3e14-4d58-bcfa-82f8d33c9f9a")

con2server = connect_remote(Sockets.localhost, 27765; session_id=session_id)

# update module in RemoteREPL when it changes in Pluto
@use_task([]) do
   mod = :nothing_yet
   while true
      newval = Symbol("workspace#", Main.PlutoRunner.moduleworkspace_count[])
      if newval != mod
         mod = newval
         remote_module!(Core.eval(Main, mod))
      end
      
      sleep(0.05)
   end
end

Then open a repl and do:

julia> using RemoteREPL, Sockets, UUIDs

julia> connect_repl(Sockets.localhost, 27765; session_id=UUID("f03aec15-3e14-4d58-bcfa-82f8d33c9f9a"))

Since the session's module is being regularly updated by the Pluto notebook, your REPL will be in sync with the notebook's state.

Troubleshooting connection issues

Sometimes errors will be encountered. This section aims to show some errors experienced by users, and what the underlying problem was. We will use some terms in this section, introduced in the table below.

TermExplanation
"local REPL"a REPL running on the same computer as the host. This could mean connecting two julia instances running on the same computer.
"remote REPL"a REPL running on a different computer than the host.
"address"a placeholder for the address you connect to, typically an IP-address. Examples of what an actual address could look like include "pi@192.168.4.2" and "youruser@example.com".

Error: IOError: connect: connection refused (ECONNREFUSED)

This error has been encountered when

  1. Running connect_repl() or connect_remote(), while attempting to connect to a local REPL. The problem was that no local REPL had previously run serve_repl(). To fix this, run serve_repl() in the local REPL.
  2. Running connect_remote(), while attempting to connect to a remote REPL. The problem was that no address was provided. To fix this, pass an address as a string to connect_remote, as in connect_remote("address")

Error: RemoteREPL stream was closed while reading header

This error has been encountered when running connect_remote("address") or connect_repl("address"), while attempting to connect to a remote REPL. The problem was that the remote REPL had not previously run serve_repl(). To fix this, run serve_repl() in the remote REPL.

Error: Bad owner or permissions on /home/username/.ssh/config

This error is raised by this line of code, from OpenSSH. The requirements translates to that "the config file must be owned by root or by the user running the ssh and can not be writable by any group or other users." (Quoted from this thread). The fix is therefore to remove write permissions for any group or other users. On a linux system, this is accomplished by running the following code.

chmod go-w /home/username/.ssh/*

If you are using a different operating system, please google how to remove write permissions on files, and try to do the same thing.