Connect to a Nerves system using SSH

Is it possible to connect to a Nerves system using SSH? My goal for now is to get to the IEx prompt over SSH instead of over an FTDI cable. This will be useful especially after the unit is installed in a relatively inaccessible location.

I know that Nerves.Firmware.SSH — nerves_firmware_ssh v0.4.6 existsbut that appears to only be about deploying/updating firmware over SSH.


Have you tried remeshing? it is in my opinion much more stable.
There are also implementations for accessing the console over ssh.
here is one example of setting it up


Late reply, but that is definitely helpful. Although not very straightforward to follow since it’s a pure elixir article, referencing an erlang article. Would be nice to have something Nerves specific. After some playing around I have a decent setup, maybe I’ll write it up later.

1 Like

Even later reply. If you setup nerves_firmware_ssh, you should be then able to SSH (by default on port 8989) directly into the Erlang console. From there, just type 'Elixir.IEx':start(). to start an IEx console.

In fact, nerves_firmware_ssh makes use of the Erlang SSH module mentioned in the link posted above.

Hope this helps :slight_smile:


I am using a separate SSH daemon, which runs the IEx shell:

system_dir = Nerves.Firmware.SSH.Application.system_dir()
{:ok, _ref} = :ssh.daemon(22, password: 'password', system_dir: system_dir, shell: {IEx, :start, []})

It’s not particulary secure in that way, but gets the job done.


Also, to prevent unauthorized access, you can specify either a user_dir option, pointing to a directory in your system containing the authorized_keys file (what in standard SSH is normally ~/.ssh), or a key_cb module, implementing an Erlang behavior to validate authorized keys.


Thanks all, I’ve gotten it working now.

I have this little snippet that you can run from init. When you SSH to port 8081 you get an Elixir shell and when you SSH to port 8082 you get an erlang shell. The main catch is you need to compile your authorized key into the firmware. It’s been working quite well for local testing though!

  defp start_ssh_server() do
    authorized_keys =
      Application.get_env(:nerves_firmware_ssh, :authorized_keys, [])
      |> Enum.join("\n")

    decoded_authorized_keys = :public_key.ssh_decode(authorized_keys, :auth_keys)

    cb_opts = [authorized_keys: decoded_authorized_keys]
    # TODO: Try using a different network directory
    system_dir = :code.priv_dir(:nerves_firmware_ssh)

    {:ok, _ref} =
      :ssh.daemon(8081, [
            {:key_cb, {Nerves.Firmware.SSH.Keys, cb_opts}},
            {:system_dir, system_dir},
            shell: {IEx, :start, []},
      # Don't specify shell so we can get an erlang shell
      :ssh.daemon(8082, [
            {:key_cb, {Nerves.Firmware.SSH.Keys, cb_opts}},
            {:system_dir, system_dir},
1 Like

Thanks, that tip is very helpful!