ssh2incus – SSH server for Incus instances
Beta notice: Breaking changes may occur between releases while the project remains in beta. Review the changelog before upgrading.
ssh2incus
provides a full-featured SSH server that connects directly to
Incus containers and virtual machines. It runs on the Incus host
and intelligently routes incoming SSH connections to the appropriate instances using the Incus API, eliminating the
need to run SSH servers inside the instances.
On-Demand Instance Creation:
+
prefix (e.g., ssh +test01@host
)~
prefix (auto-delete on poweroff)ssh +test+ubuntu/24.04+m4+c2+d20+nest+priv@host
create-config.yaml
Flexible Authentication:
--password-auth
)--inauth
)--auth-methods
)--noauth
)Multiple Remotes: Connect to any remote from incus remote list
Terminal Support: Full PTY (terminal) mode and remote command execution
File Transfer: Complete SCP and SFTP support with integrated SFTP server
Port Forwarding:
ssh -L
)ssh -R
)ssh -D
)SSH Agent Forwarding: Seamlessly forward your SSH agent into instance sessions
Process Models:
Incus Shell: Manage Incus over SSH
Terminal Session Management:
%
prefix (survives SSH disconnections)tmux
or screen
)System Integration:
create-config.yaml
)Compatibility:
Download the latest package from the Releases page and install:
apt-get install -f ./ssh2incus_0.8-0_amd64.deb
dnf install ./ssh2incus-0.8-0.x86_64.rpm
Start and enable the service:
systemctl enable ssh2incus.service
systemctl start ssh2incus.service
Monitor logs:
journalctl -f -u ssh2incus.service
To establish an SSH connection to an instance running on Incus host, run:
ssh -p 2222 [%][remote:][instance-user@]instance-name[.project-name][~host-user]@incus-host
For creating new instances on-demand (can be enabled using --allow-create
flag), use:
ssh -p 2222 [+|~][remote:]instance-name[.project-name][+%profile1][+%profile2][+image][+memory][+cpu][+disk][+options][~host-user]@incus-host
Where:
instance-name
: Name of an instance (required)remote
: Remote name from incus remote list
(optional, defaults to either current remote or remote set via -r
flag)instance-user
: User in the Incus instance (optional, defaults to root
)project-name
: Incus project name (optional, defaults to default
)host-user
: User on the Incus host (optional, defaults to root
)incus-host
: Hostname or IP address of the Incus host where ssh2incus
is running (required)Special Prefixes:
%
: Use persistent terminal session (tmux/screen) - reconnects to existing session if available+
: Create new persistent instance if it doesn't exist~
: Create new ephemeral instance (deleted on poweroff) if it doesn't existInstance Creation Options (when using +
or ~
):
%profile
: Apply configuration profile (e.g., %web-server
, %database
)image
: Container/VM image (e.g., ubuntu/24.04
, alpine/edge
)mN
: Memory in GiB (e.g., m2
for 2GB, m4
for 4GB)cN
: CPU cores (e.g., c2
for 2 cores, c4
for 4 cores)dN
: Disk size in GiB (e.g., d10
for 10GB, d0
for unlimited)nest
or n
: Enable nested virtualizationpriv
or p
: Enable privileged containervm
or v
: Create VM instead of containerNote: The login string parsing has been significantly improved in v0.8 with enhanced error handling, better validation, and comprehensive test coverage. The parser now handles complex scenarios more reliably while maintaining full backward compatibility with existing login string formats.
Connect to existing instance ubuntu
as root
:
ssh -p 2222 ubuntu@1.2.3.4
Connect to instance ubuntu
as root using admin
on the host:
ssh -p 2222 ubuntu~admin@1.2.3.4
Connect to instance ubuntu
as user ubuntu
using host user admin
:
ssh -p 2222 ubuntu@ubuntu~admin@1.2.3.4
Connect to instance ubuntu
in project1
as user ubuntu
:
ssh -p 2222 ubuntu@ubuntu.project1@1.2.3.4
Connect to instance ubuntu
in project1
on remote incus-prod
as user ubuntu
:
ssh -p 2222 incus-prod:ubuntu@ubuntu.project1@1.2.3.4
Connect to instance with persistent terminal session (survives SSH disconnections):
ssh -p 2222 %ubuntu@test01@1.2.3.4
Create new persistent instance with defaults:
ssh -p 2222 +test01@1.2.3.4
Create ephemeral instance with defaults (deleted on instance poweroff):
ssh -p 2222 ~test02@1.2.3.4
Create Ubuntu 24.04 container with 2GB RAM, 2 CPUs, 10GB disk:
ssh -p 2222 +test03+ubuntu/24.04+m2+c2+d10@1.2.3.4
Create privileged nested container with specific configuration:
ssh -p 2222 +test04+alpine/edge+m4+c4+d20+nest+priv@1.2.3.4
Create ephemeral VM with Ubuntu 24.04:
ssh -p 2222 ~vm01+ubuntu/24.04+m4+c2+d20+vm@1.2.3.4
Create instance in specific project with custom image:
ssh -p 2222 +test05.myproject+ubuntu/22.04+m2+c1+d15@1.2.3.4
Create instance on specific remote:
ssh -p 2222 +remote1:test06+alpine/edge+m1+c1+d5@1.2.3.4
Create instance using configuration profiles:
ssh -p 2222 +web01+%web-server+%database@1.2.3.4
Create instance with multiple profiles and resource overrides:
ssh -p 2222 +app01+%base+%nodejs+ubuntu/24.04+m8+c4@1.2.3.4
Complex Example: Create ephemeral privileged nested VM with Ubuntu 24.04, 4GB RAM, 4 CPUs, 20GB disk on remote prod
in project testing
:
ssh -p 2222 ~prod:myvm.testing+ubuntu/24.04+m4+c4+d20+vm+nest+priv@1.2.3.4
ssh2incus supports on-demand instance creation through special SSH login syntax. This feature allows you to create and connect to new Incus instances without pre-existing them.
For detailed documentation on advanced configuration options, profiles, and file includes, see the Instance Creation Configuration Guide.
+
prefix)Creates a new persistent instance that will remain available until manually deleted:
ssh -p 2222 +instance-name@incus-host
~
prefix)Creates a new ephemeral instance that is automatically deleted when the instance is powered off:
ssh -p 2222 ~instance-name@incus-host
Instance creation supports various configuration options specified after the +
separator:
Specify the container/VM image to use:
ssh -p 2222 +test01+ubuntu/24.04@incus-host
ssh -p 2222 +test02+alpine/edge@incus-host
Note: For cloud-init support, use images with the /cloud
suffix:
ssh -p 2222 +test01+ubuntu/24.04/cloud@incus-host
ssh -p 2222 +test02+alpine/edge/cloud@incus-host
Apply predefined configuration profiles:
ssh -p 2222 +test01+%web-server@incus-host
ssh -p 2222 +test02+%database+%monitoring@incus-host
mN
- Memory in GiB (e.g., m2
, m4
, m8
)cN
- Number of CPU cores (e.g., c1
, c2
, c4
)dN
- Root disk size in GiB (e.g., d10
, d20
, d0
for unlimited)ssh -p 2222 +test03+ubuntu/24.04+m4+c2+d20@incus-host
nest
or n
: Enable nested virtualization (for running containers/VMs inside)priv
or p
: Enable privileged container (enhanced permissions)vm
or v
: Create virtual machine instead of containerssh -p 2222 +test04+ubuntu/24.04+m2+c2+d10+nest+priv@incus-host
ssh -p 2222 +vm01+ubuntu/24.04+m4+c4+d30+vm@incus-host
When no configuration options are specified, instances are created using defaults from /etc/ssh2incus/create-config.yaml
:
version: 1
image: alpine/edge
ephemeral: false
memory: 1 # 1 GiB
cpu: 1 # 1 CPU core
disk: 10 # 10 GiB
vm: false # Container by default
devices: {}
config:
security.privileged: false
security.nested: false
security.syscalls.intercept.mknod: false
security.syscalls.intercept.setxattr: false
create-config.yaml
%profile1+%profile2
)incus delete instance-name
incus stop instance-name
, incus start instance-name
poweroff
, shutdown -h now
incus stop instance-name
# Quick Ubuntu development environment
ssh -p 2222 +dev-env+ubuntu/24.04+m4+c2+d20@incus-host
# Temporary Alpine testing environment
ssh -p 2222 ~test-alpine+alpine/edge+m1+c1+d5@incus-host
# Docker-enabled environment with nesting
ssh -p 2222 +docker-host+ubuntu/24.04+m8+c4+d50+nest+priv@incus-host
# Windows-compatible VM environment
ssh -p 2222 +win-compat+ubuntu/24.04+m8+c4+d100+vm@incus-host
Enable SSH agent forwarding to use your local SSH keys inside the instance:
eval `ssh-agent`
ssh -A -p 2222 ubuntu@1.2.3.4
ssh2incus
automatically creates a proxy socket device in the instance and removes it when the connection closes.
Enable password authentication for SSH connections:
# Enable password authentication
ssh2incus --password-auth
# Or combine with public key auth using method chains
ssh2incus --auth-methods "publickey,password"
When password authentication is enabled, ssh2incus
will verify passwords against the system's user database within the target instance.
ssh2incus
supports persistent terminal sessions using terminal multiplexers. Sessions remain active even when SSH connections are closed.
# Connect to persistent session (tmux will be automatically installed if missing)
ssh -p 2222 ubuntu@1.2.3.4
# Configure to use tmux explicitly
ssh2incus --term-mux tmux
# Configure to use screen instead of tmux
ssh2incus --term-mux screen
The system automatically:
Forward local port 8080 to port 80 on the instance:
To forward local port 8080
listening on 127.0.0.1
to port 80
on ubuntu
instance, run:
ssh -L 127.0.0.1:8080::80 -p 2222 ubuntu@1.2.3.4
Forward remote port 3000 on the instance to local port 8080 on your machine:
ssh -R 127.0.0.1:3000:127.0.0.1:8080 -p 2222 ubuntu@1.2.3.4
Dynamic port forwarding sets up a SOCKS5 proxy server through your SSH connection, allowing applications to route traffic through it regardless of destination port:
ssh -D 1080 -p 2222 ubuntu@1.2.3.4
ssh2incus
includes a built-in SFTP server that supports standard OpenSSH SFTP server options for secure file transfers.
--chroot-sftp
flag to enable chrooting SFTP sessions to the user's home directory for enhanced security isolation, except for root users who are not chrootedEnable SFTP chroot for better security isolation:
# Enable SFTP chroot to home directory
ssh2incus --chroot-sftp
# This restricts SFTP users to their home directory and subdirectories
# Upload files using SCP
scp -P 2222 localfile.txt root@host:/path/to/destination/
# Upload files using SFTP
sftp -P 2222 root@host
sftp> put localfile.txt /path/to/destination/
sftp> quit
# Download files
scp -P 2222 root@host:/path/to/file.txt ./downloaded-file.txt
The built-in SFTP server supports all standard SFTP operations including directory navigation, file uploads/downloads, and permission management while providing additional security through chroot capabilities.
You can access Incus instances through the Incus host acting as an SSH proxy or bastion.
Configure this in your ~/.ssh/config
:
Host incus1
Hostname localhost
Port 2222
ProxyJump incus-host
Host incus-host
Hostname 1.2.3.4
User root
Now connect to the ubuntu
instance as root
with:
ssh ubuntu@incus1
Security Note: Using this method provides additional security benefits since port 2222 is not exposed to the public internet.
ssh2incus
offers two operating modes to suit different environments and requirements:
Master process mode employs a primary process that spawns child processes for handling connections. This architecture provides:
ssh2incus
service is restartedssh2incus
service without disrupting active sessionsThis is the recommended mode for production environments or systems with sufficient resources.
Daemon mode runs as a single process with multiple threads, designed for:
To enable daemon mode, modify /etc/default/ssh2incus
: remove -m
from ARGS=
.
Note: In daemon mode, all active connections will be terminated if the
ssh2incus
service is restarted.
The /shell
command provides direct access to the Incus command line interface from an SSH session,
allowing you to manage your Incus instances without needing to log into the host directly.
Only root is allowed to connect to Incus shell.
This feature is especially useful for:
To access the Incus shell, connect using:
ssh -p 2222 /shell@incus-host
$ ssh /shell@incus-colima
incus shell emulator on colima-incus (Ctrl+C to exit)
Hit Enter or type 'help <command>' for help about any command
Type incus command:
> incus version
Client version: 6.16
Server version: 6.16
Type incus command:
> incus
ssh2incus
fully supports Ansible automation for container management, making it simple to orchestrate your
Incus instance configuration.
Create an ansible.cfg
file with optimized settings for Incus instances:
[defaults]
host_key_checking = False
remote_tmp = /tmp/.ansible-${USER}
Connect directly to ssh2incus
on port 2222:
[incus1]
# Basic instance connection
instance-a ansible_user=c1 ansible_host=1.2.3.4 ansible_port=2222
# Connection with privilege escalation
instance-b ansible_user=u1@ubuntu ansible_host=1.2.3.4 ansible_port=2222 become=yes
Use SSH configuration for a cleaner approach (requires ProxyJump configuration in ~/.ssh/config
):
[incus2]
# Basic instance connection
instance-c ansible_user=c1 ansible_host=incus1
# Connection with privilege escalation
instance-d ansible_user=u1@ubuntu ansible_host=incus1 become=yes
---
- hosts: incus1,incus2
become: no
become_method: sudo
tasks:
- command: env
- command: ip addr
By default, ssh2incus
:
2222
root
and members of the incus
, incus-admin
groupsTo grant a user access permission:
sudo usermod -aG incus your-host-user
ssh2incus
can be configured in two ways:
Configuration File: Create a config.yaml
file in one of these locations (checked in order):
./config.yaml
$HOME/.config/ssh2incus/config.yaml
/etc/ssh2incus/config.yaml
Command Line Flags: Add configuration flags to the ARGS=
line in /etc/default/ssh2incus
Note: Command line flags have higher priority than configuration file options.
The configuration file uses YAML format with all options commented out by default (using system defaults). Each setting corresponds to a command-line flag:
# Listen on ":port" or "host:port" (flag: --listen, -l)
# listen: ":2222"
# Enable debug log (flag: --debug, -d)
# debug: false
# Enable password authentication (flag: --password-auth, -P)
# password-auth: false
# List of groups members of which allowed to connect (flag: --groups, -g)
# groups: "incus,incus-admin"
...
Uncomment and modify any options you want to change from their defaults.
-C, --allow-create allow creating new instances
--auth-methods string enable auth method chain, e.g.: "publickey,password"
-b, --banner show banner on login
--chroot-sftp enable home dir chroot for SFTP sessions
-c, --client-cert string client certificate for remote
-k, --client-key string client key for remote
-d, --debug enable debug log
--dump dump parsed config
--dump-create dump parsed create config
-g, --groups string list of groups members of which allowed to connect (default "incus,incus-admin")
-H, --healthcheck string enable Incus health check every X minutes, e.g. "5m"
-h, --help print help
--idle-timeout duration idle session timeout duration, e.g. 180s or 5m (default 3m0s)
--inauth use --instance-auth
-I, --instance-auth enable authentication using instance keys
-l, --listen string listen on ":port" or "host:port" (default ":2222")
-m, --master start master process and spawn workers
--noauth disable SSH authentication completely
-P, --password-auth enable password authentication
--pprof enable pprof
--pprof-listen string pprof listen on ":port" or "host:port" (default ":6060")
-r, --remote string default Incus remote to use
-t, --server-cert string server certificate for remote
-S, --shell string shell access command: login, su, sush or user shell (default)
-s, --socket string Incus socket to connect to (optional, defaults to INCUS_SOCKET env)
-T, --term-mux string terminal multiplexer: tmux (default) or screen (default "tmux")
-u, --url string Incus remote url to connect to (should start with https://)
-v, --version print version
-w, --welcome show welcome message to users connecting to shell
Enable debug logging and restrict listening to localhost:
ARGS=-d -l 127.0.0.1:2222
Enable password authentication with public key fallback:
ARGS=--auth-methods "publickey,password"
Use screen instead of tmux for persistent sessions:
ARGS=--term-mux screen
Combine multiple options:
ARGS=-d --password-auth --term-mux tmux --banner
Enable the optional welcome banner with the -b
flag:
┌──────────────────────────────────────────────┐
│ _ ____ _ │
│ ___ ___| |__ |___ \(_)_ __ ___ _ _ ___ │
│ / __/ __| '_ \ __) | | '_ \ / __| | | / __| │
│ \__ \__ \ | | |/ __/| | | | | (__| |_| \__ \ │
│ |___/___/_| |_|_____|_|_| |_|\___|\__,_|___/ │
└──────────────────────────────────────────────┘
👤 root 📦 a9.default 💻 colima-incus
────────────────────────────────────────────────
You can customize the SSH banner and welcome messages by creating banner.txt
and welcome.txt
files in your configuration directory. The server looks for these files in standard configuration locations.
Both banner and welcome message files support template variables that are automatically substituted:
[INSTANCE_USER]
: Current instance user name[INSTANCE]
: Instance name[PROJECT]
: Project name[REMOTE]
: Remote server name[HOSTNAME]
: System hostnameExample banner (banner.txt.example
):
┌──────────────────────────────────────────────┐
│ _ ____ _ │
│ ___ ___| |__ |___ \(_)_ __ ___ _ _ ___ │
│ / __/ __| '_ \ __) | | '_ \ / __| | | / __| │
│ \__ \__ \ | | |/ __/| | | | | (__| |_| \__ \ │
│ |___/___/_| |_|_____|_|_| |_|\___|\__,_|___/ │
└──────────────────────────────────────────────┘
👤 [INSTANCE_USER] 📦 [INSTANCE].[PROJECT] 💻 [REMOTE]/[HOSTNAME]
────────────────────────────────────────────────
Example welcome message (welcome.txt.example
):
Welcome "[INSTANCE_USER]" to incus shell on [INSTANCE].[PROJECT]
The banner provides useful context showing:
ssh2incus
v0.8 supports instance configuration templates via create-config.yaml
files. These templates provide default settings for on-demand instance creation using the +
and ~
login prefixes, ensuring consistent instance deployment while allowing customization through the login syntax.
For comprehensive documentation on configuration profiles, file includes, and advanced instance creation workflows, see the Instance Creation Configuration Guide.
The system looks for configuration files in these locations (in order):
./create-config.yaml
(current working directory)$HOME/.config/ssh2incus/create-config.yaml
(home configuration directory)/etc/ssh2incus/create-config.yaml
(system-wide default)These templates are automatically used when creating instances with +
or ~
prefixes. Any options specified in the SSH login string will override the template defaults.
# create-config.yaml - Default instance creation template
version: 1
image: alpine/edge # Default container image
ephemeral: false # Create persistent instances by default
memory: 1 # 1 GiB RAM
cpu: 1 # 1 CPU core
disk: 10 # 10 GiB root disk
vm: false # Container by default
# Instance devices
devices: {}
# Instance configuration
config:
security.privileged: false # Non-privileged by default
security.nesting: false # No nesting by default
security.syscalls.intercept.mknod: false # Docker compatibility
security.syscalls.intercept.setxattr: false # Docker compatibility
user.user-data: "!include cloud-init.yaml" # Load cloud-init from file
# Configuration profiles for different use cases
profiles:
web-server:
image: ubuntu/24.04/cloud # /cloud suffix required for cloud-init
memory: 2
cpu: 2
disk: 20
config:
security.nesting: true
user.user-data: "<@profiles/web-server-init.yaml"
database:
image: ubuntu/24.04/cloud # /cloud suffix required for cloud-init
memory: 4
cpu: 2
disk: 50
config:
security.privileged: true
user.user-data: "!include profiles/database-setup.sh"
development:
image: ubuntu/24.04
memory: 8
cpu: 4
disk: 100
config:
security.nesting: true
security.privileged: true
The template provides defaults that are automatically applied during instance creation. Configuration options are applied in this order:
defaults
section# Uses all template defaults
ssh -p 2222 +test01@incus-host
# Applies web-server profile configuration
ssh -p 2222 +test02+%web-server@incus-host
# Applies both profiles (database settings override web-server where they conflict)
ssh -p 2222 +test03+%web-server+%database@incus-host
# Uses development profile but overrides memory and CPU
ssh -p 2222 +test04+%development+m16+c8@incus-host
# Creates ephemeral instance with profile (overrides template's ephemeral: false)
ssh -p 2222 ~test05+%web-server@incus-host
Configuration values support file includes for loading external content:
!include filename.ext
- Include file contents as the configuration value<@filename.ext
- Alternative include syntax for file contentsFiles are resolved in this order:
create-config.yaml
Example:
config:
user.user-data: "!include cloud-init.yaml" # Loads ./cloud-init.yaml
user.meta-data: "<@metadata/instance-info.yaml" # Loads ./metadata/instance-info.yaml
This system enables consistent instance deployment across environments while maintaining full flexibility for specific use cases through profiles and the SSH login syntax.
Since ssh2incus
listens on port 2222
by default, you'll need to configure your firewall to allow incoming connections on this port.
# Allow SSH access on port 2222
sudo ufw allow 2222/tcp
# Apply changes
sudo ufw reload
# Verify the rule was added
sudo ufw status
# Allow SSH access on port 2222
sudo firewall-cmd --permanent --add-port=2222/tcp
# Apply changes
sudo firewall-cmd --reload
# Verify the rule was added
sudo firewall-cmd --list-ports
sudo iptables -A INPUT -p tcp --dport 2222 -j ACCEPT
sudo iptables-save > /etc/iptables/rules.v4 # Make changes persistent
sudo nft add rule inet filter input tcp dport 2222 accept
sudo nft list ruleset > /etc/nftables.conf # Make changes persistent
For production environments, consider these firewall best practices:
ssh2incus
only to your management networkGet help from the community and developers:
For organizations requiring guaranteed response times and dedicated assistance:
If you encounter issues with ssh2incus
:
journalctl -u ssh2incus.service
systemctl status ssh2incus.service
telnet incus-host 2222
-d
flag in /etc/default/ssh2incus
incus
or incus-admin
)