Skip to content

Instantly share code, notes, and snippets.

@diraneyya
Last active October 3, 2025 10:30
Show Gist options
  • Select an option

  • Save diraneyya/137530405ece8cd4c2cef4a9a9fb65b6 to your computer and use it in GitHub Desktop.

Select an option

Save diraneyya/137530405ece8cd4c2cef4a9a9fb65b6 to your computer and use it in GitHub Desktop.
A jump script for the mobile shell (Mosh) when used to connect to Proxmox containers/VMs via its root PVE node
#!/usr/bin/env bash
# The public IP address or the Dynamic DNS hostname of the
# proxmox host, which can be resolved to a public IP address
PROXMOX_HOST=host.dyndns.org
# A single TCP port that is used for initiating a connection
PROXMOX_TCP_PORT=2222
# A range of UDP ports that can be used for each Mosh session
PROXMOX_UDP_PORTS=60001:60008
PROXMOSH_BIN=/root/.local/bin
PROXMOSH_LOG=$PROXMOSH_BIN/proxmosh.log
# This is a new feature I added considering how painful are
# the qm and the pct commands to use. Instead, now you have
# the choice of configuring SSH so that it can be used instead
# to access the container/VM from the root PVE node.
USE_SSH_WHEN_POSSILE=1
declare -A container=(
[singular]='LXC container'
[plural]='LXC containers'
[base_command]='pct'
[list_subcommand]='list'
[jump_subcommand]='console'
)
declare -A vm=(
[singular]='VM'
[plural]='VMs'
[base_command]='qm'
[list_subcommand]='list'
[jump_subcommand]='terminal'
)
technologies=(container vm)
find_target() {
# the first argument contains the name of the VM or the container
if [[ -z "$1" ]]; then return 1; fi
local return_code
local output
for ref in "${technologies[@]}"; do
declare -n tech="$ref"
output=$("${tech[base_command]}" \
"${tech[list_subcommand]}" | awk -v name="$1" '
function get_field(field_name) {
temp = substr($0, col_pos[field_name])
match(temp, /\S+/)
value = substr(temp, RSTART, RLENGTH)
return value
}
NR == 1 {
for (i = 1; i <= NF; i++) {
col_pos[tolower($i)] = index($0, $i)
}
exit_code = -1
next
}
get_field("name") == name {
vmid = get_field("vmid")
status = get_field("status")
if (status != "running") {
print "Error: '"${tech[singular]}"' " name " is " status > "/dev/stderr"
exit_code = 1
} else {
print vmid
exit_code = 0
}
}
END {
if (exit_code == -1) {
exit_code = 2;
}
exit exit_code
}')
return_code=$?
if [[ $return_code -eq 0 ]]; then
declare -gir TARGET_ID="$output";
declare -gr TARGET_TECH="$ref";
fi
if [[ $return_code -ne 2 ]]; then
break
fi
done
if [[ $return_code -eq 2 ]]; then
local plural_terms=()
for ref in "${technologies[@]}"; do
declare -n tech="$ref"
plural_terms+=("${tech[plural]}")
done
local IFS='/'
echo "Error: no ${plural_terms[*]} were found using the name '$1'." >&2
fi
return $return_code
}
running_in_proxmox=1
for ref in "${technologies[@]}"; do
declare -n tech="$ref"
if ! command -v "${tech[base_command]}" &>/dev/null; then
running_in_proxmox=
fi
done
if [[ -n "$running_in_proxmox" ]]; then
target_invoked=
if [[ -n "$1" ]]; then
if [[ -n "$USE_SSH_WHEN_POSSILE" ]]; then
ssh -T "$1" true &>/dev/null
if [[ $? -eq 0 ]]; then
target_invoked=1
exec ssh "$1"
fi
fi
if [[ -z "$target_invoked" ]] && find_target "$1"; then
mkdir -p "$PROXMOSH_BIN"
declare -n tech="$TARGET_TECH"
jump_command="${tech[base_command]} ${tech[jump_subcommand]}"
echo "$(date): $jump_command '$TARGET_ID'" >> "$PROXMOSH_LOG"
target_invoked=1
$jump_command "$TARGET_ID"
fi
fi
if [[ -z "$target_invoked" ]]; then
exec /usr/bin/env bash -l
fi
else
ssh -p $PROXMOX_TCP_PORT $PROXMOX_HOST "mkdir -p $PROXMOSH_BIN"
ssh_command="ssh -p $PROXMOX_TCP_PORT"
if command -v rsync &>/dev/null; then
rsync -az -e "$ssh_command" "$0" $PROXMOX_HOST:"$PROXMOX_BIN/proxmosh"
elif command -v scp &>/dev/null; then
scp -P "$PROXMOX_TCP_PORT" "$0" $PROXMOX_HOST:"$PROXMOSH_BIN"
else
echo "Error: script requires either 'rsync' or 'scp' to be installed." >&2
exit 1
fi
if [[ $? -ne 0 ]]; then
exit 1
fi
mosh --ssh="$ssh_command" -p "$PROXMOX_UDP_PORTS" $PROXMOX_HOST -- "$PROXMOSH_BIN/proxmosh" "${1:-}"
fi
@diraneyya
Copy link
Author

diraneyya commented Oct 2, 2025

Setup: Change the values of PROXMOX_HOST, PROXMOX_TCP_PORT, and PROXMOX_UDP_PORTS to match your setup.

Usage: proxmosh to connect via Mosh to the root node, and proxmosh name to connect to a container/VM with the name name. In case of preferring SSH, be sure to add the public PVE node SSH certificate to authorized_hosts on the container/VM and configure the user you wish to log in using in /root/.ssh/config on the root PVE node.

For more information, check the supporting tutorial at https://forum.proxmox.com/threads/use-mobile-shell-mosh-with-proxmox.173209/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment