An Euler tunnel connects an interactive tool on your computer, such as VS Code, to an Euler compute node reserved by a Slurm job. Use it only after ordinary SSH access to Euler works. It is not needed for normal unattended batch jobs.
Goal: connect VS Code or SSH to a Slurm compute allocation instead of doing computation on an Euler login node.
Prerequisites: the final public-key-only config-ok alias test in
Euler access passes. Do not continue if ssh euler still
needs the ETH password.
New to text commands? Read how to open a terminal and run one command before using a command block. It explains the labels used below and what to copy.
Execution context: euler-tunnel config/start/status/stop run on an Euler
login-node Bash prompt. SSH files and the tunnel hostname test run on the local
computer in the explicitly labelled PowerShell or zsh/Bash block.
What euler-tunnel Does#
euler-tunnel start submits a Slurm job containing an SSH server. Your local
Host euler-tunnel entry reaches that allocated compute node through the Euler
login service. Installing .vscode-server in your Euler home directory on the
first VS Code connection is expected.
Use a tunnel for interactive editing, notebooks, setup, and short debugging.
Use sbatch for unattended training, sweeps, and long computation.
1. Generate Personal Configuration#
Run on an Euler login node - Bash:
euler-tunnel config
Expected output:
- one
euler-tunnel ssh-ed25519 ...known-hosts line; - one
Host euler-tunnelconfiguration block containing your username and aProxyCommand.
The generated host key is personal. Copy the output from your own account; do not copy another student's known-hosts line.
2. Add The Known-Hosts Line Locally#
Fetch your own generated output through the verified Host euler alias. The
commands below extract only the host-key line, back up an existing file, and do
not append a duplicate.
Windows laptop - PowerShell#
& {
$SshDir = Join-Path $env:USERPROFILE ".ssh"
$KnownHosts = Join-Path $SshDir "known_hosts"
New-Item -ItemType Directory -Force $SshDir | Out-Null
$TunnelOutput = ssh euler "euler-tunnel config"
if ($LASTEXITCODE -ne 0) { throw "Could not obtain euler-tunnel configuration" }
$HostKeyLines = @($TunnelOutput | Where-Object { $_ -match '^euler-tunnel\s+ssh-' })
if ($HostKeyLines.Count -ne 1) { throw "Expected exactly one euler-tunnel host-key line" }
if (Test-Path $KnownHosts) {
Copy-Item $KnownHosts "$KnownHosts.backup.$(Get-Date -Format yyyyMMdd-HHmmss)"
}
if (-not (Test-Path $KnownHosts) -or
-not (Select-String -Path $KnownHosts -SimpleMatch $HostKeyLines[0] -Quiet)) {
if (Test-Path $KnownHosts) {
$Existing = [System.IO.File]::ReadAllText($KnownHosts)
if ($Existing.Length -gt 0 -and -not $Existing.EndsWith("`n")) {
[System.IO.File]::AppendAllText($KnownHosts, [Environment]::NewLine)
}
}
Add-Content $KnownHosts $HostKeyLines[0] -Encoding ascii
}
ssh-keygen -F euler-tunnel -f $KnownHosts
}
macOS or Linux laptop - zsh/Bash#
(
set -eu
ssh_dir="$HOME/.ssh"
known_hosts="$ssh_dir/known_hosts"
mkdir -p "$ssh_dir"
chmod 700 "$ssh_dir"
tunnel_output="$(ssh euler 'euler-tunnel config')"
host_key_lines="$(printf '%s\n' "$tunnel_output" | \
grep -E '^euler-tunnel[[:space:]]+ssh-')"
if [ "$(printf '%s\n' "$host_key_lines" | sed '/^$/d' | wc -l | tr -d ' ')" -ne 1 ]; then
printf 'STOP: expected exactly one euler-tunnel host-key line.\n' >&2
exit 1
fi
if [ -f "$known_hosts" ]; then
cp -p "$known_hosts" "$known_hosts.backup.$(date +%Y%m%d-%H%M%S)"
fi
touch "$known_hosts"
if ! grep -Fqx "$host_key_lines" "$known_hosts"; then
if [ -s "$known_hosts" ] &&
[ "$(tail -c 1 "$known_hosts" | wc -l | tr -d ' ')" -eq 0 ]; then
printf '\n' >> "$known_hosts"
fi
printf '%s\n' "$host_key_lines" >> "$known_hosts"
fi
chmod 600 "$known_hosts"
ssh-keygen -F euler-tunnel -f "$known_hosts"
)
Expected result: ssh-keygen prints one euler-tunnel host-key entry.
3. Add The Tunnel Host In The Existing Include Directory#
The Euler access procedure created ~/.ssh/passport.d/. Use a separate file so
the Host euler block cannot be joined accidentally to the tunnel block.
Windows laptop - PowerShell#
& {
$SshDir = Join-Path $env:USERPROFILE ".ssh"
$IncludeDir = Join-Path $SshDir "passport.d"
$BackupDir = Join-Path $SshDir "passport-backups"
$TunnelConfig = Join-Path $IncludeDir "euler-tunnel.conf"
$TunnelTemporary = "$TunnelConfig.passport-new"
$Resolved = @(ssh -G euler 2>$null)
if ($LASTEXITCODE -ne 0) { throw "STOP: the euler alias does not parse" }
$UserMatch = $Resolved | Select-String '^user\s+' | Select-Object -First 1
$EulerUser = if ($UserMatch) { ($UserMatch.Line -split '\s+', 2)[1] } else { "" }
$IdentityFiles = @($Resolved | ForEach-Object { if ($_ -match '^identityfile\s+(.+)$') { $Matches[1] } })
$IdentitiesOnlyMatch = $Resolved | Select-String '^identitiesonly\s+' | Select-Object -First 1
$IdentitiesOnly = if ($IdentitiesOnlyMatch) { ($IdentitiesOnlyMatch.Line -split '\s+', 2)[1] } else { "" }
if ($EulerUser -notmatch '^[A-Za-z0-9._-]+$') { throw "STOP: the euler alias has no valid short username" }
if ($IdentityFiles.Count -eq 0 -or @($IdentityFiles | Where-Object { $_ -eq 'none' -or $_ -match '\.pub$' }).Count -gt 0) { throw "STOP: the euler alias has no safe private identity selection" }
if ($IdentitiesOnly -notin @('yes', 'no')) { throw "STOP: the euler alias has no valid identity policy" }
$MarkerLines = @(ssh -o PreferredAuthentications=publickey -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no euler "echo config-ok")
$Marker = if ($MarkerLines.Count -gt 0) { ([string]$MarkerLines[-1]).Trim() } else { "" }
if ($LASTEXITCODE -ne 0 -or $Marker -ne 'config-ok') { throw "STOP: the required config-ok alias test failed" }
New-Item -ItemType Directory -Force $IncludeDir, $BackupDir | Out-Null
if (Test-Path $TunnelConfig) {
Copy-Item $TunnelConfig (Join-Path $BackupDir "euler-tunnel.conf.$(Get-Date -Format yyyyMMdd-HHmmss)")
}
$Lines = @("Host euler-tunnel", " User $EulerUser")
foreach ($Identity in $IdentityFiles) {
if ($Identity.Contains('"')) { throw "STOP: unsupported quote in resolved identity path" }
$Lines += ' IdentityFile "' + $Identity + '"'
}
$Lines += @(
" IdentitiesOnly $IdentitiesOnly",
" PreferredAuthentications publickey",
" PasswordAuthentication no",
" KbdInteractiveAuthentication no",
" ForwardAgent no",
" ServerAliveInterval 10",
" ServerAliveCountMax 10",
" ProxyCommand ssh -o PreferredAuthentications=publickey -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no euler euler-tunnel connect"
)
$Lines | Set-Content $TunnelTemporary -Encoding ascii
ssh -F $TunnelTemporary -G euler-tunnel | Out-Null
if ($LASTEXITCODE -ne 0) {
Remove-Item $TunnelTemporary -ErrorAction SilentlyContinue
throw "STOP: generated tunnel config is invalid; the existing file is unchanged"
}
Move-Item -Force $TunnelTemporary $TunnelConfig
ssh -G euler-tunnel | Select-String '^(user|identityfile|identitiesonly|preferredauthentications|passwordauthentication|kbdinteractiveauthentication|forwardagent|proxycommand) '
if ($LASTEXITCODE -ne 0) { throw "STOP: tunnel alias is invalid; restore the timestamped backup" }
}
Do not add the generated ControlMaster, ControlPath, or ControlPersist
lines on Windows; they use Unix sockets.
macOS or Linux laptop - zsh/Bash#
(
set -eu
include_dir="$HOME/.ssh/passport.d"
backup_dir="$HOME/.ssh/passport-backups"
tunnel_config="$include_dir/euler-tunnel.conf"
tunnel_temporary="$tunnel_config.passport-new"
resolved="$(ssh -G euler 2>/dev/null)" || { printf 'STOP: the euler alias does not parse\n' >&2; exit 1; }
eth_user="$(printf '%s\n' "$resolved" | awk '$1 == "user" { print $2; exit }')"
case "$eth_user" in ''|*[^ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-]*) printf 'STOP: the euler alias has no valid short username\n' >&2; exit 1;; esac
identity_files="$(printf '%s\n' "$resolved" | awk '$1 == "identityfile" { $1=""; sub(/^[[:space:]]+/, ""); print }')"
[ -n "$identity_files" ] || { printf 'STOP: the euler alias has no identity selection\n' >&2; exit 1; }
if printf '%s\n' "$identity_files" | awk '$0 == "none" || $0 ~ /[.]pub$/ { bad=1 } END { exit !bad }'; then
printf 'STOP: the euler alias has no safe private identity selection\n' >&2
exit 1
fi
identities_only="$(printf '%s\n' "$resolved" | awk '$1 == "identitiesonly" { print $2; exit }')"
case "$identities_only" in yes|no) ;; *) printf 'STOP: the euler alias has no valid identity policy\n' >&2; exit 1;; esac
marker="$(ssh -o PreferredAuthentications=publickey -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no euler 'echo config-ok')" || {
printf 'STOP: the required config-ok alias test failed\n' >&2
exit 1
}
[ "$marker" = config-ok ] || { printf 'STOP: the required config-ok marker was not returned\n' >&2; exit 1; }
mkdir -p "$include_dir" "$backup_dir"
chmod 700 "$include_dir" "$backup_dir"
if [ -f "$tunnel_config" ]; then
cp -p "$tunnel_config" "$backup_dir/euler-tunnel.conf.$(date +%Y%m%d-%H%M%S)"
fi
{
printf 'Host euler-tunnel\n User %s\n' "$eth_user"
while IFS= read -r identity; do
[ -n "$identity" ] || continue
case "$identity" in *'"'*) printf 'STOP: unsupported quote in resolved identity path\n' >&2; exit 1;; esac
printf ' IdentityFile "%s"\n' "$identity"
done <<EOF
$identity_files
EOF
printf ' IdentitiesOnly %s\n' "$identities_only"
printf ' PreferredAuthentications publickey\n PasswordAuthentication no\n KbdInteractiveAuthentication no\n ForwardAgent no\n'
printf ' ServerAliveInterval 10\n ServerAliveCountMax 10\n'
printf ' ProxyCommand ssh -o PreferredAuthentications=publickey -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no euler euler-tunnel connect\n'
printf ' ControlMaster auto\n ControlPath ~/.ssh/cs-%%r@%%h:%%p\n ControlPersist 15\n'
} > "$tunnel_temporary"
chmod 600 "$tunnel_temporary"
ssh -F "$tunnel_temporary" -G euler-tunnel >/dev/null || {
rm -f "$tunnel_temporary"
printf 'STOP: generated tunnel config is invalid; the existing file is unchanged\n' >&2
exit 1
}
mv "$tunnel_temporary" "$tunnel_config"
ssh -G euler-tunnel | grep -E '^(user|identityfile|identitiesonly|preferredauthentications|passwordauthentication|kbdinteractiveauthentication|forwardagent|proxycommand) '
)
Expected result: the resolved user, private identity selection, and
identitiesonly policy match the tested euler alias; password and
keyboard-interactive authentication are no, forwarding is no, and the
proxy command reaches the existing euler alias. If a tunnel
configuration already existed, it was backed up and replaced only after the
new file parsed successfully.
4. Start The Smallest Suitable Allocation#
Run these only on an Euler login node - Bash.
CPU-only interactive work on es_fuge#
euler-tunnel start --account=es_fuge --cpus-per-task=2 --mem-per-cpu=4G --time=02:00:00
CPU-only public-share work#
euler-tunnel start --account=public --cpus-per-task=2 --mem-per-cpu=4G --time=02:00:00
Short RTX 4090 debugging#
euler-tunnel start --account=es_fuge --gpus=rtx_4090:1 --cpus-per-task=4 --mem-per-cpu=4G --time=00:30:00
Short RTX 3090 debugging#
euler-tunnel start --account=es_fuge --gpus=rtx_3090:1 --cpus-per-task=4 --mem-per-cpu=4G --time=00:30:00
The smaller interactive CPU/memory profile is intentional. Use the batch starter profile in Slurm GPU jobs for representative training, then right-size from measurements.
RTX 4090 is the standard GPU tunnel choice. The lab also has access to two RTX
PRO 6000 GPUs, but they require explicit pro_6000 selection and compatible
CUDA 13 software. Use their
special-purpose batch profile
rather than treating them as an interactive fallback.
Check on the Euler login node - Bash:
euler-tunnel status
5. Prove The Tunnel Before Opening VS Code#
Run on your local computer:
ssh -o PreferredAuthentications=publickey -o PasswordAuthentication=no -o KbdInteractiveAuthentication=no euler-tunnel "hostname"
Expected output: a compute-node hostname such as eu-g5-047-2. A key
passphrase prompt is normal. An ETH password prompt means the upstream Host euler key setup is not working; stop and fix it.
6. Connect VS Code#
- Install the Microsoft Remote - SSH extension locally.
- Open the Command Palette.
- Select Remote-SSH: Connect to Host....
- Select
euler-tunnel. - Wait for VS Code Server to install or start in your Euler home directory.
- Open your project clone, not the whole
/cluster/work/fugetree.
Only If Terminal SSH Works But VS Code Fails#
Do not change SSH files when the local tunnel test in step 5 still fails. If that test succeeds but VS Code does not connect:
- In VS Code, open View > Output and select Remote - SSH.
- Check which local SSH configuration file the log says it is using.
- If it is not the existing user file that contains
Include ~/.ssh/passport.d/*.conf, open local User Settings, search for Remote.SSH: Config File, and enter the full path to that same existing file (normally~/.ssh/config). - Retry Remote-SSH: Connect to Host... > euler-tunnel.
This setting only tells VS Code which existing file to read; it does not replace or regenerate the file. Do not weaken password settings or add unrelated Remote-SSH compatibility toggles to make the prompt disappear. A key passphrase is expected; an ETH password prompt is not.
The terminal prompt should be a compute node. Verify in the VS Code terminal:
hostname
echo "$SLURM_JOB_ID"
Stop The Allocation#
Run on an Euler login node - Bash when interactive work ends:
euler-tunnel stop
Common Failures And Safe Recovery#
- Tunnel asks for ETH password: repeat the public-key-only
config-okalias test. Host euler-tunnelis malformed: runssh -G euler-tunnel; each SSH keyword must be on its own line.- Private-key warning names
.pub: remove.pubfromIdentityFile. - VS Code prints download/install logs: wait for
listeningOnand successful startup. This is normal on first connection. - Terminal tunnel works but VS Code fails: follow the config-file check in step 6; do not regenerate a working SSH key.
- No tunnel job exists: start it on the Euler login node before connecting.
Ask For Help When#
config-ok succeeds and euler-tunnel status shows a running job, but the local
SSH tunnel test still fails. Share the relevant ssh -v euler-tunnel hostname
error lines only after removing usernames, private paths, and host details
output, never key contents.
Primary Source#
Verified: 2026-08-04. Review by: 2026-11-04. Owner: IDEAL Lab IT.