Launch and connect securely to Jupyter kernels on remote machines via SSH with minimal configuration, as if they were local.
- ℹ️ Note
- Currently, Windows is not supported as neither local nor remote machine.
- Quick Start
- Installation
- Managing SSH Jupyter Kernels Specifications
- SSH Configuration
- Launching Remote Kernels from Command Line
- Development
- Troubleshooting
- Implementation Details
- Historical Note
Get up and running with sshpyk in minutes:
- Install sshpyk on your local machine
pip install sshpyk
See Installation for more details.
- Add a DEDICATED alias configuration entry in your SSH
config
file (typically~/.ssh/config
) with the following settings:
# You can name the alias anything you want. We recommend to include "sshpyk"
# in the name to remind yourself that this is a DEDICATED alias for sshpyk.
Host remote_server_sshpyk
HostName 192.168.1.100 # EDIT THIS
User my_user_name_on_remote_server # EDIT THIS
IdentityFile ~/.ssh/private_key_for_remote_server # EDIT THIS
BatchMode yes
# WARNING: ControlMaster/ControlPath/ControlPersist are mandatory and should be
# under a *DEDICATED* host alias, otherwise you will experience bad side effects
ControlMaster auto # must be `auto`
ControlPath ~/.ssh/sshpyk_%r@%h_%p # dir must exist!
ControlPersist 10m
# Other recommended configurations
StrictHostKeyChecking no
ServerAliveInterval 5
ServerAliveCountMax 120000
TCPKeepAlive yes
ConnectionAttempts 1
ConnectTimeout 5
- 💡 Tip
If you already have an ssh host alias configured. You can prefix your host alias name with a wildcard
*
and then define a dedicated host alias prefixed with, e.g.,_sshpyk
as follows. (Make sure the wildcard does not match other hosts unintentionally!)Host remote_server* HostName 192.168.1.100 # EDIT THIS User my_user_name_on_remote_server # EDIT THIS IdentityFile ~/.ssh/private_key_for_remote_server # EDIT THIS # Other recommended configurations StrictHostKeyChecking no ServerAliveInterval 5 ServerAliveCountMax 120000 TCPKeepAlive yes ConnectionAttempts 1 ConnectTimeout 5 # Inherits the rest of the config from `remote_server*` Host remote_server_sshpyk # WARNING: ControlMaster/ControlPath/ControlPersist are mandatory and should be # under a *DEDICATED* host alias, otherwise you will experience bad side effects ControlMaster auto # must be `auto` ControlPath ~/.ssh/sshpyk_%r@%h_%p # dir must exist! ControlPersist 10m
With this config you can ssh into your remote as usual with remote_server
for all
the purposes you are already used to. While remote_server_sshpyk
will be used
exclusively for sshpyk
without interfering with your other ssh sessions.
See Recommended SSH Config for more details.
- Ensure you have SSH access to your remote server and public key authentication is set up, you must connect without password prompt:
ssh -o BatchMode=no remote_server_sshpyk "echo CONNECTED"
See Authentication via Private/Public Key for setting up SSH keys.
If you are sure that the remote sshd
does not allow authentication via private/public key see Authentication via Password.
- Add a remote kernel (replace values with your configuration):
sshpyk add --ssh-host-alias remote_server_sshpyk \
--kernel-name ssh_remote_python3 \
--display-name "Remote Python 3.10" \
--remote-python /path/to/python/env \
--remote-kernel-name python3 \
--language python
See Adding a Remote Kernel for all available options.
- Start JupyterLab and select your new remote kernel for a notebook/console:
jupyter lab
- Your code now runs on the remote server and your local notebook interfaces with it!
You can install sshpyk using pip:
pip install sshpyk
For development installation:
pip install -e ".[dev]"
Requirements:
- On the local system:
sshpyk
andjupyter_client
- On the remote system:
jupyter_client
sshpyk
provides a command-line interface to manage remote Jupyter kernels via SSH tunnels:
$ sshpyk --help
usage: sshpyk [-h] [--verbose] {list,add,edit,delete} ...
Manage SSH Jupyter kernels (version 0.0)
positional arguments:
{list,add,edit,delete}
Command to execute
list List available kernels
add Add a new SSH kernel
edit Edit an existing SSH kernel
delete Delete a kernel
options:
-h, --help show this help message and exit
--verbose, -v Increase logs verbosity (-v for warning, -vv for info, -vvv for debug)
You can list all available kernels using the list
command:
$ sshpyk list --help
usage: sshpyk list [-h] [--remote] [--local] [--no-check]
options:
-h, --help show this help message and exit
--remote, -r List only remote SSH kernels
--local, -l List only local kernels
--no-check, -n Skip remote kernel checks
$ sshpyk list
---- Local Kernel ----
Name: f310
Display Name: Python 3.10
Resource Dir: /Users/victor/Library/Jupyter/kernels/f310
Command: /opt/homebrew/anaconda3/envs/f310/bin/python -m ipykernel_launcher -f {connection_file}
Language: python
Interrupt Mode: signal
---- Local Kernel ----
Name: ir
Display Name: R
Resource Dir: /opt/homebrew/anaconda3/envs/g/share/jupyter/kernels/ir
Command: R --slave -e IRkernel::main() --args {connection_file}
Language: R
Interrupt Mode: signal
----- SSH Kernel -----
Name: demo_remote
Display Name: Python 3.9 (Remote Demo)
Kernel spec: /Users/victor/Library/Jupyter/kernels/demo_remote/kernel.json
Command (simplified): ssh sshpyk_mba sshpyk-kernel --SSHKernelApp.kernel_name=python3 ...
Language: python
Interrupt Mode: (v) message
SSH Path: (v) /opt/homebrew/bin/ssh
SSH Host Alias: sshpyk_mba
(i) user: victor
(i) hostname: 192.168.238.4
(v) batchmode: yes
(v) identityfile: /Users/victor/.ssh/id_rsa_for_localhost
(v) controlmaster: auto
(v) controlpersist: 600
(v) controlpath: /Users/victor/.ssh/sshpyk_victor@192.168.238.4_22
(i) proxyjump: sshpyk_jump
SSH Host Alias: sshpyk_jump (jump)
(i) user: root
(i) hostname: 81.82.23.179
(v) batchmode: yes
(v) identityfile: /Users/victor/.ssh/id_rsa_for_sshpyk_jump
(v) controlmaster: auto
(v) controlpersist: 600
(v) controlpath: /Users/victor/.ssh/sshpyk_root@81.82.23.179_53456
Remote Script Dir: (v) $HOME/.ssh/sshpyk (/Users/victor/.ssh/sshpyk)
SSH Connection: (v) sshpyk_mba
Remote System: Darwin MacBook-Air 20.5.0 Darwin Kernel Version 20.5.0: Sat May 8 05:10:33 PDT 2021; root:xnu-7195.121.3~9/RELEASE_X86_64 x86_64
Remote Interrupt Mode: signal
Remote Python: (v) /usr/local/anaconda3/envs/f39/bin/python
Remote Kernel Name: (v) python3
Launch Timeout: 15
Shutdown Timeout: 15
Remote Command: python -m ipykernel_launcher -f {connection_file}
29649 2025-05-15 17:16:58,306 ERROR sshpyk.utils utils:309 verify_ssh_connection: [sshpyk_mbp stderr] ssh: Could not resolve hostname sshpyk_mbp: nodename nor servname provided, or not known
29649 2025-05-15 17:16:58,306 ERROR sshpyk.utils utils:325 verify_ssh_connection: SSH connection to 'sshpyk_mbp' failed (exit code=255).
----- SSH Kernel -----
Name: ssh_mbp_ext
Display Name: Python 3.13 (RMBP)
Kernel spec: /Users/victor/Library/Jupyter/kernels/ssh_mbp_ext/kernel.json
Command (simplified): ssh sshpyk_mbp sshpyk-kernel --SSHKernelApp.kernel_name=python3 ...
Language: python
Interrupt Mode: (v) message
SSH Path: (v) /opt/homebrew/bin/ssh
SSH Host Alias: sshpyk_mbp
(x) identityfile: Likely missing in your ssh config. Multiple values: ['~/.ssh/id_rsa', '~/.ssh/id_ecdsa', '~/.ssh/id_ecdsa_sk', '~/.ssh/id_ed25519', '~/.ssh/id_ed25519_sk', '~/.ssh/id_xmss'].
(i) user: victor
(x) hostname: Likely missing in your ssh config. host='sshpyk_mbp' and hostname='sshpyk_mbp' must be different.
(!) batchmode: Recommended to be 'yes', not 'no'.
(x) controlmaster: Must be 'auto', not 'false'.
(x) controlpersist: Must be, e.g., '10m' or 'yes', not 'no'.
(x) controlpath: Missing, use, e.g., '~/.ssh/sshpyk_%r@%h_%p'.
Remote Script Dir: (?) $HOME/.ssh/sshpyk
SSH Connection: (x) sshpyk_mbp
Remote Python: (?) /opt/homebrew/anaconda3/envs/g/bin/python
Remote Kernel Name: (?) python3
Launch Timeout: 15
Shutdown Timeout: 15
29649 2025-05-15 17:16:58,337 ERROR sshpyk.utils utils:309 verify_ssh_connection: [sshpyk_mbp_ext stderr] ssh: Could not resolve hostname sshpyk_mbp_ext: nodename nor servname provided, or not known
29649 2025-05-15 17:16:58,337 ERROR sshpyk.utils utils:325 verify_ssh_connection: SSH connection to 'sshpyk_mbp_ext' failed (exit code=255).
----- SSH Kernel -----
Name: ssh_mbp_ext_broken
Display Name: Python 3.13 (RMBP Broken)
Kernel spec: /Users/victor/Library/Jupyter/kernels/ssh_mbp_ext_broken/kernel.json
Command (simplified): ssh sshpyk_mbp_ext sshpyk-kernel --SSHKernelApp.kernel_name=python3 ...
Language: python
Interrupt Mode: (v) message
SSH Path: (v) /opt/homebrew/bin/ssh
SSH Host Alias: sshpyk_mbp_ext
(x) identityfile: Likely missing in your ssh config. Multiple values: ['~/.ssh/id_rsa', '~/.ssh/id_ecdsa', '~/.ssh/id_ecdsa_sk', '~/.ssh/id_ed25519', '~/.ssh/id_ed25519_sk', '~/.ssh/id_xmss'].
(i) user: victor
(x) hostname: Likely missing in your ssh config. host='sshpyk_mbp_ext' and hostname='sshpyk_mbp_ext' must be different.
(!) batchmode: Recommended to be 'yes', not 'no'.
(x) controlmaster: Must be 'auto', not 'false'.
(x) controlpersist: Must be, e.g., '10m' or 'yes', not 'no'.
(x) controlpath: Missing, use, e.g., '~/.ssh/sshpyk_%r@%h_%p'.
Remote Script Dir: (?) $HOME/.ssh/sshpyk
SSH Connection: (x) sshpyk_mbp_ext
Remote Python: (?) /opt/homebrew/anaconda3/envs/g/bin/python
Remote Kernel Name: (?) python3
Launch Timeout: 15
Shutdown Timeout: 15
To add a new remote kernel, use the add
command. For a remote kernel to work:
sshpyk
must be installed on the local system (which depends onjupyter_client
explicitly)jupyter_client
must be installed on the remote system
Here's the help information for the add
command:
$ sshpyk add --help
You can modify an existing kernel using the edit
command:
$ sshpyk edit --help
- 💡 Pro tip
- If you are familiar with Jupyter kernel specifications, you can edit the
kernel.json
specifications manually in theResource Dir
for quick changes.
To remove a kernel, use the delete
command:
$ sshpyk delete --help
The --ssh-host-alias
parameter refers to host aliases defined in your SSH config
file, not IP addresses.
These aliases, among other advantages, provide a convenient way to group connection
settings under a Host alias_name
entry.
This simplifies making an SSH connection to just $ ssh alias_name
and have the
SSH client use the settings defined under its Host alias_name
entry.
For simplicity and maximum flexibility, sshpyk
does not manage any of the SSH config
options.
Instead we have a Recommended SSH Config below.
Your SSH configuration is typically stored in $HOME/.ssh/config
.
We recommend a wildcard host alias and a DEDICATED host alias named such that it
matches the wildcard (or simply a dedicated host alias as shown in Quick start):
Host remote_server*
# Required config: HostName/User/IdentityFile
# ##################################################################################
# IP address of the remote system
HostName 192.168.1.100 # EDIT THIS
# Your unix username on the remote system
User my_user_name_on_remote_server # EDIT THIS
# Required for automated login, see `Authentication via Private/Public Key`_
# for more details
IdentityFile ~/.ssh/private_key_for_remote_server # EDIT THIS
# `BatchMode yes` prevents ssh from asking for interactive input.
# E.g., when a password prompt is required for successful connection.
# You can skip it if you REALLY cannot use any alternative to password-based
# authentication. In such case, you have to automate the password prompt.
# Not recommended unless you know how to communicate the password securely.
BatchMode yes
# ##################################################################################
# Connection stability:
# ServerAliveInterval/ServerAliveCountMax/TCPKeepAlive/ConnectionAttempts/ConnectTimeout
# ##################################################################################
# Send a "heartbeat" to the server every ServerAliveInterval seconds, if no reply,
# wait ServerAliveCountMax attempts before giving up.
ServerAliveInterval 5
# Set some big value, e.g. ServerAliveInterval * ServerAliveCountMax = ~7 days
ServerAliveCountMax 120000
TCPKeepAlive yes
# Shorter ConnectionAttempts/ConnectTimeout helps to reconnect to the kernel faster
# when e.g. loosing internet connection temporarily. However if connecting to your
# remote host is expected to take a long time, you might need to increase these.
ConnectionAttempts 1
ConnectTimeout 5
# ##################################################################################
# The port on the remote system that SSH server is listening on (22 is the default)
Port 22
# Optional, slightly less secure but recommended for this type of automation:
StrictHostKeyChecking no
# ... rest of your config, if you know what you are doing
# You can suffix the alias with anything you want. We recommend to include "sshpyk"
# in the name to remind yourself that this is a dedicated alias for sshpyk.
Host remote_server_sshpyk
# Isolation, performance, responsiveness: ControlMaster/ControlPath/ControlPersist
# ##################################################################################
# Reuse existing connections to the remote server, this speeds up new connections
# to the remote server by reusing a "master" connection. If a master connection
# is already established, it will be used, otherwise a new one will be created.
# `auto` option is also essential for reusing an ssh connection established manually
# e.g. when the remote host requires a password and explicitly forbids private key
# authentication.
ControlMaster auto # must be `auto`
# The path to the control socket, this is used to manage the connection to the
# remote server. Make sure to not use the same ControlPath for other host non-sshpyk
# aliases! This is to avoid conflicts with other SSH connections and session to the
# same machine. Sharing the same control socket with other non-sshpyk related SSH
# sessions might have unintended side effects.
# Make sure the dirs on the path to the control socket exist, otherwise unrelated
# errors might happen in sshpyk.
ControlPath ~/.ssh/sshpyk_%r@%h_%p # dir must exist!
# Keep the master connection "warm" after the last time the SSH connection was used.
# For connection stability and to speed up kernel restarts.
# Note that there will be some SSH process on your local machine still running for
# after the kernel shutdown. This is expected and harmless.
# When the remote host requires a password, set ControlPersist to a large value,
# e.g. `200h` to avoid having to restart the master connection manually and input
# the host password.
ControlPersist 10m
# ##################################################################################
With this configuration, you can use remote_server_sshpyk
as your --ssh-host-alias
in sshpyk
commands.
⚠️ Warning- Make sure that your alias name in the SSH
config
does not match any other alias "wildcards" in your SSHconfig
unintentionally. For example, if you have an aliasremote_*
in your SSHconfig
, these settings can affect theremote_server_sshpyk
as well, which might lead to unexpected behavior. ‼️ ImportantControlMaster: auto
is mandatory forsshpyk
to work. We highly recommend using the suggestedControlPersist
,ControlPath
,BatchMode yes
,ServerAliveInterval
,ServerAliveCountMax
, andTCPKeepAlive
settings. This is to ensure that your SSH connection is stable and does not get dropped unexpectedly. With these settings your connection to the remote kernel should survive, e.g., losing your WiFi connection for a few minutes, and perhaps even longer.
sshpyk
expects ssh
commands to run without password prompts.
We recommend using private/public key-based SSH authentication.
You must set up SSH key authentication for all remote hosts you intend to use.
To set up SSH key-based authentication:
- Generate an SSH key pair on your local machine (if you don't already have one):
# Don't set a passphrase for the key when prompted.
ssh-keygen -t ed25519 -f ~/.ssh/private_key_for_remote_server -C "some comment for your own reference"
- Check your private key is accessible without a passphrase:
ssh-keygen -y -f ~/.ssh/private_key_for_remote_server
- Copy your public key to the remote server:
ssh-copy-id -o BatchMode=no remote_username@some.remote.server.com
Or manually add the contents of ~/.ssh/private_key_for_remote_server.pub
from your local machine to the authorized_keys
file on the remote machine. Typically ~/.ssh/authorized_keys
, but please consult your remote system's administrator for the correct location/procedure.
- Add the key to your SSH config (edit to match your own setup):
Host remote_server_sshpyk
HostName some.remote.server.com
User remote_username
IdentityFile ~/.ssh/private_key_for_remote_server
BatchMode yes
# ... the rest of the config as described in `Recommended SSH Config`
- Test your connection, you should connect without being prompted for a password:
ssh -o BatchMode=no remote_server_sshpyk "echo CONNECTED"
If you're having trouble connecting even after setting up SSH keys, here are some common culprits:
- Incorrect Permissions on the Remote Server:
- Your home directory (e.g., /home/username) on the server should not be writable by others (chmod 755 or drwxr-xr-x is typical).
- The
~/.ssh
directory on the server must have strict permissions, typically 700 (drwx------
). Usechmod 700 ~/.ssh
. - The
~/.ssh/authorized_keys
file on the server must also have strict permissions, typically 600 (-rw-------
). Usechmod 600 ~/.ssh/authorized_keys
.
- Public Key Issues:
- The public key content in
~/.ssh/authorized_keys
on the server does not exactly match the corresponding private key, or it's the wrong public key. - The public key in
authorized_keys
is malformed (e.g., incomplete copy, extra line breaks, missing parts). Ensure it's a single, unbroken line of text, usually starting withssh-rsa
,ssh-ed25519
, etc. - Multiple public keys in
authorized_keys
should each be on a new line.
- Client-Side Private Key & Configuration Issues:
- The
IdentityFile
directive in your local~/.ssh/config
points to the wrong private key file, a non-existent file, or the public key file instead of the private key. - The private key file on your local machine has incorrect permissions. It should typically be 600 (
-rw-------
) or 400 (-r--------
). Usechmod 600 /path/to/your/private_key
. - If your private key is protected by a passphrase, an SSH agent (like
ssh-agent
) must be running and have the key added (ssh-add /path/to/your/private_key
), especially ifBatchMode yes
is used in your SSH config, as this prevents interactive passphrase prompts (as intended but can be a source of confusion).
- SSH Server Configuration (sshd_config on the Remote Server):
PubkeyAuthentication
might be set tono
in the server's/etc/ssh/sshd_config
file. It should beyes
. Check with your remote system's administrator.- The
AuthorizedKeysFile
directive insshd_config
might point to a non-standard location for the authorized keys file (e.g.,.ssh/authorized_keys2
). Ensure your public key is in the correct file. Check with your remote system's administrator. - User-specific restrictions like
AllowUsers
,DenyUsers
,AllowGroups
, orDenyGroups
insshd_config
might be preventing your user from logging in. Check with your remote system's administrator. - The SSH daemon (
sshd
) on the server might need to be reloaded or restarted after changes tosshd_config
. Your remote system's administrator should know how to do this.
- SSH Agent Issues on the Client:
- The
ssh-agent
is not running on your local machine. - The correct private key has not been added to the
ssh-agent
(usessh-add -l
to list added keys, andssh-add /path/to/private_key
to add one). This applies mainly to passphrase-protected keys. - Too many keys have been offered to the server (especially if you have many keys in your agent or specified via
IdentityFile
), and the server has given up before trying the correct one. You can useIdentitiesOnly yes
in your~/.ssh/config
for the specific host to ensure only the specifiedIdentityFile
is used. - When debugging, use verbose output from the SSH client (e.g.,
ssh -vvv remote_server_sshpyk
) to get detailed information about the connection attempt, including which keys are being offered and where the authentication process might be failing.
If the remote sshd
is configured to specifically only allow password authentication,
you can still use sshpyk
by either:
- Changing the
sshd
configuration to allow private/public key-based authentication (ask your system administrator); or - Manually establishing a master SSH connection before attempting to start any
sshpyk
kernels, as described in Authentication via Password; or - Spawning a
sshd
on the remote system on a custom port configured to allow private/public key-based authentication and following the instructions above.
If your remote host doesn't allow private/public key-based authentication and insists
on password authentication, you can still use sshpyk
by manually establishing a
master SSH connection before attempting to start any sshpyk
kernels:
- In your SSH config, set a long
ControlPersist
value (orControlPersist=yes
for an indefinite persistence) to avoid frequent manual password prompts:
Host sshpyk_password_server
HostName password.example.com
User remote-username
# Skip `BatchMode yes` if you setup an automated password-based authentication.
# Not recommended unless you know how to communicate the password securely.
BatchMode yes
ControlMaster auto
ControlPath ~/.ssh/sshpyk_%r@%h_%p
# Set a very long persistence time or ControlPersist=yes for indefinite persistence
ControlPersist 200h
# ... the rest of the config as described in `Recommended SSH Config`
- Manually establish the master connection before attempting to start any
sshpyk
kernels:
# -M = ControlMaster
# -f = go to background
# -N = do not execute a command on the remote server
ssh -M -f -N sshpyk_password_server
# You'll be prompted for your password
⚠️ Warning- When using password authentication, if the master connection process dies,
which happens if you disconnect from internet for a bit (e.g. unstable WiFi),
you need to manually run
ssh -M -f -N sshpyk_password_server
again to input your password. Afterwards the connection to the remote kernel should be smoothly reestablished.
- Now add and use your sshpyk kernel as normal, without needing to enter your password again:
sshpyk add --ssh-host-alias sshpyk_password_server --kernel-name ssh_remote_python3 ...
The ControlMaster
connection will remain active for the duration specified in ControlPersist
,
allowing sshpyk
to use it seamlessly despite the password requirement.
In rare situations where the remote server only supports password authentication and you have not other alternative but to automate the password authentication in order to be able to use sshpyk
. This is highly discouraged due to significant security risks.
If you find yourself in this situation, the dangerous
directory within the sshpyk
repository contains an example script (ssh-sshpass-wrapper
) and a sample SSH config. This script demonstrates using sshpass
to automate the password input to login into a Bastion host. Under the hood, from that Bastion host a final ssh jump to the target remote server is made using the ProxyJump
feature of SSH. The authentication to the target remote server is done using a normal private key.
Proceed with extreme caution and diligence:
- Understand the security implications of storing and handling passwords programmatically.
- This approach is less secure than key-based authentication because
ssh
won't be enforcing file permissions, etc., on the customssh-sshpass-wrapper
script. - The example script and configuration are provided as a proof-of-concept and require modifications for your specific environment, etc.
- Make sure you exhausted all the possible reasons why the key-based authentication is not working. You can find some common reasons in Common Reasons for Private Key Authentication Failure.
- Consult the
dangerous/README.md
file for more details before attempting this method.
One powerful SSH feature is the ability to connect to hosts behind a bastion (jump) server. For example in your SSH config you would add the following dedicated alias entries:
Host sshpyk_bastion
HostName bastion.example.com
User bastion-username
IdentityFile ~/.ssh/id_rsa_bastion # required for automated login
BatchMode yes
# ... the rest of the config as described in `Recommended SSH Config`
Host sshpyk_internal_server
HostName internal-server.example.com
User remote-username
IdentityFile ~/.ssh/id_rsa_internal # required for automated login
BatchMode yes
ProxyJump sshpyk_bastion # this is the key line that enables the "jump" through the bastion
# ... the rest of the config as described in `Recommended SSH Config`
‼️ Important- For connection stability and performance, we highly recommend using the settings described in Recommended SSH Config along with using dedicated alias entries.
This configuration allows you to:
- Connect first to
bastion.example.com
asbastion-username
- Then tunnel through to
internal-server.example.com
asremote-username
When using sshpyk
, you would simply specify --ssh-host-alias sshpyk_internal_server
and the SSH tunneling will be handled automatically according to your SSH config
file.
‼️ Important- Remember that SSH automatic authentication must be set up for both
sshpyk_bastion
andsshpyk_internal_server
, either via SSH private/public key-based authentication or password authentication, as described in Authentication via Private/Public Key and Authentication via Password, respectively. - 💡 Tip
- You can of course have as many bastion hosts between you and the remote server as you want.
The sshpyk-kernel
command is a command-line utility to launch remote kernels and manage their lifecycle.
It uses the same provisioning system as the SSHKernelProvisioner
but can be invoked directly to support use cases outside of Jupyter.
$ sshpyk-kernel --help
When running in an interactive terminal, you can use Ctrl+D
to show a menu to shutdown, interrupt, restart, or leave the command without shutting down the kernel.
More information will be printed in the logs when running the command.
The sshpyk-kernel
command supports kernel persistence through the following options:
--persistent
: If True, the remote kernel will be left running on shutdown so you can reconnect to it later.--persistent-file
: Path to save persistence info. If provided,--persistent
is overridden to True. A default path will be used if not provided.--existing
: Connect to an existing kernel using a previously saved persistence info file.--leave
: Launch the kernel and exit command right away.
Example of creating a persistent kernel:
# Create a persistent kernel
sshpyk-kernel --kernel=demo_remote --persistent
Later, reconnect to the same kernel (the path will be printed in the logs of the previous command):
sshpyk-kernel --kernel=demo_remote --existing=sshpyk-kernel-1c9ce85b-f722-41e5-970a-13cfdd44fbfb.json
- ℹ️ Note
--existing
here is a path to a persistence file created bysshpyk-kernel
, NOT the typical jupyter connection file!
You can interact with the kernel using e.g. jupyter-console
(a jupyter client launches an ipython
shell):
pip install jupyter-console # if not already installed
jupyter-console --existing=kernel-a3b70f44-6b9a-4f82-a6b8-dd736f04b888.json
- ℹ️ Note
--existing
here is a path to the local connection file, in the typical jupyter connection file format. It is NOT the persistence file created bysshpyk-kernel
. Similarly, this path is printed in the logs of thesshpyk-kernel
command.- 💡 Tip
- You can press
Ctrl+D
in thejupyter-console
to leave the application without shutting down the kernel. Callingexit()
/quit()
in theipython
shell or a notebook will still shutdown the kernel. This is expected behavior. The remoteSSHKernelApp
python script will detect this and shutdown itself.
When running in an interactive terminal, you can use:
Ctrl+D
: Shows a menu to interrupt, shutdown, restart, or leave the command without shutting down the kernelCtrl+C
: Interrupts the kernelCtrl+\
(backslash): Leaves the application without shutting down the kernel
If you invoke sshpyk-kernel
from a non-interactive shell, you can use signals to control the kernel:
SIGTERM
: Shuts down the kernel, unless--persistent
or--persistent-file
have been passedSIGHUP
: Shuts down the kernel, unless--persistent
or--persistent-file
have been passedSIGINT
: Interrupts the kernelSIGUSR1
: Restarts the kernelSIGUSR2
: Shuts down the remote kernel, ignoring--persistent
or--persistent-file
SIGQUIT
: Leaves the application without shutting down the kernelSIGKILL
: this signal cannot be caught, it will kill the local command without any local nor remote cleanup. Not recommended. Use only as last resort.
The command is designed to work with Jupyter's kernel specification system.
When you add a remote kernel using sshpyk add
, the command is automatically configured in the kernel spec file (kernel.json
).
This allows applications external to Jupyter the jupyter ecosystem to launch the remote kernel and connect to it.
Example kernel.json
created by sshpyk add
:
{
"argv": [
"/opt/homebrew/anaconda3/envs/g/bin/python",
"/opt/homebrew/anaconda3/envs/g/bin/sshpyk-kernel",
"--SSHKernelApp.kernel_name=demo_remote",
"--KernelManager.connection_file='{connection_file}'"
],
"display_name": "Python 3.9 (Remote Demo)",
"language": "python",
"interrupt_mode": "message",
"metadata": {
"kernel_provisioner": {
"provisioner_name": "sshpyk-provisioner",
"config": {
"ssh": null,
"ssh_host_alias": "sshpyk_mba",
"remote_python": "/usr/local/anaconda3/envs/f39/bin/python",
"remote_kernel_name": "python3"
}
}
}
}
The demo.py
in the repository provides a complete example of how to use sshpyk
programmatically:
- Launch a (persistent) remote kernel
- Execute interactive code on it
- Reconnect to the same kernel later
- Clean up resources
For more information on interacting with the kernel programmatically,
see the jupyter_client documentation
or consult the provisioning.py
source code for some inspiration.
In a Python 3.8+ environment:
pip install -e ".[dev]"
# installs the python package in editable mode- Reload your shell, e.g. open the terminal again.
pre-commit install
- Make your changes to the files and test them.
git commit -m "your message"
, this will run the pre-commit hooks defined in.pre-commit-config.yaml
. If your code has problems it won't let you commit.
To auto-format code, apply other small fixes (e.g. trailing whitespace) and to lint all the code:
pre-commit run --all-files
If you are running into issues, try first to restart your system(s) if possible 😉. Debugging SSH connections can be tricky.
Running the sshpyk list
by default will check the remote kernels and the corresponding SSH connections (you can use --no-check
to skip the remote kernel checks).
Its output might already pinpoint the issue. You can pass a verbose sshpyk list -vvv
flag to get more detailed logs (or just -v
or -vv
).
Make sure you can ssh -o BatchMode=no -vvv remote_server_sshpyk "echo CONNECTED"
into your remote host without password prompts,
before attempting to launch the sshpyk
kernel.
To debug problems during kernel launch/shutdown/restart/etc., you can launch the sshpyk kernel manually with verbose logging.
Along with it, you can pass --ssh-verbose=vvv
to get most verbose logging from the ssh
commands that sshpyk
invokes.
sshpyk-kernel --kernel ssh_remote_python3 --debug --ssh-verbose=vvv
Read the logs, it will contain commands and output from the local/remote processes. You can open a new GitHub issue and share the output if you need help.
sshpyk
integrates with Jupyter Client through the kernel provisioning API introduced in jupyter_client
7.0.
It implements a custom KernelProvisionerBase
subclass called SSHKernelProvisioner
that:
- Establishes SSH connections to remote hosts
- Copies the
sshpyk-kernel
launcher script to the remote (by default into$HOME/.ssh/sshpyk/
, shell variables are expanded) - Launches kernels on remote systems
- Sets up port forwarding for kernel communication channels using
ssh -O forward -L ...
control master commands - Manages the lifecycle of the remote kernel
The provisioner is registered as an entry point in pyproject.toml
, making it available to any
Jupyter application that uses jupyter_client
.
The design of this package was initially inspired upon SSH Kernel which
in turn is based upon remote_ikernel. This implementation was
created to adapt to recent changes to jupyter_client
(which broke ssh_ipykernel
)
and to support Python 3.10+. Later it was reimplemented to integrate with jupyter_client
's provisioning system.