ssh2incus – SSH server for Incus instances
This guide provides comprehensive documentation for ssh2incus's advanced instance creation and configuration system, including profile-based configuration, file includes, and dynamic instance creation workflows.
ssh2incus includes a ready-to-use web server example that demonstrates dynamic instance creation with cloud-init configuration. This example automatically deploys a fully-configured nginx web server with a custom demo website.
The example configuration is included in the default create-config.yaml
:
profiles:
web-server-example:
image: ubuntu/22.04/cloud
memory: 2
cpu: 2
disk: 20
config:
cloud-init.user-data: <@web-server-init.example.yaml
Files involved:
/etc/ssh2incus/create-config.yaml
- Contains the web-server-example
profile/etc/ssh2incus/web-server-init.example.yaml
- Full web server deployment scriptThe example automatically:
/api/status
)Create a web server instance using the example profile:
ssh user+web-server-example@incus-host
Once deployed, access the website at http://YOUR_INSTANCE_IP/
and test the API with:
curl http://YOUR_INSTANCE_IP/api/status
DO NOT edit the example files directly - they will be overwritten on updates. Instead:
Copy the example file:
cp /path/to/web-server-init.example.yaml my-web-server-init.yaml
Create your own profile in create-config.yaml
:
profiles:
my-web-server:
image: ubuntu/22.04/cloud
memory: 2
cpu: 2
config:
cloud-init.user-data: <@my-web-server-init.yaml
Modify my-web-server-init.yaml
with your custom configuration
This example demonstrates the power of ssh2incus's file include functionality (<@
syntax) and cloud-init integration for automated instance configuration. See the sections below for more details on these features.
ssh2incus provides a powerful configuration system for creating instances with customizable settings. The system supports:
The main configuration file (create-config.yaml
) defines default settings and reusable profiles for instance creation.
defaults:
# Default settings for all instances
image: alpine/edge
ephemeral: false
memory: 2
cpu: 2
disk: 10
vm: false
devices: {}
config:
security.privileged: false
security.nesting: false
security.syscalls.intercept.mknod: false
security.syscalls.intercept.setxattr: false
profiles:
# Named configuration profiles
profile-name:
# Profile-specific overrides
Option | Type | Description | Default |
---|---|---|---|
image |
string | Container/VM image | alpine/edge |
ephemeral |
boolean | Create ephemeral instance | false |
memory |
integer | Memory limit in GiB (0 = unlimited) | 2 |
cpu |
integer | CPU limit (0 = unlimited) | 2 |
disk |
integer | Root disk size in GiB (0 = unlimited) | 10 |
vm |
boolean | Create VM instead of container | false |
devices |
object | Instance devices configuration | {} |
config |
object | Instance configuration key-value pairs | {} |
Profiles are named configuration templates that can be applied during instance creation. They allow you to define reusable sets of configuration options for different use cases.
profiles:
web-server:
image: ubuntu/24.04/cloud
memory: 4
cpu: 2
disk: 20
config:
security.nesting: true
user.user-data: |
#cloud-config
packages:
- nginx
- certbot
runcmd:
- systemctl enable nginx
- systemctl start nginx
database:
image: ubuntu/24.04/cloud
memory: 8
cpu: 4
disk: 50
config:
security.privileged: true
user.user-data: |
#cloud-config
packages:
- postgresql
- postgresql-contrib
runcmd:
- systemctl enable postgresql
- systemctl start postgresql
development:
image: ubuntu/24.04/cloud
memory: 4
cpu: 2
disk: 30
config:
security.nesting: true
security.privileged: true
user.user-data: |
#cloud-config
packages:
- build-essential
- git
- docker.io
users:
- name: developer
groups: docker
sudo: ALL=(ALL) NOPASSWD:ALL
💡 Tip: ssh2incus includes a ready-to-use
web-server-example
profile that demonstrates nginx deployment with cloud-init. See the Quick Start section above for details.
Profiles are applied using the %profile-name
syntax in SSH login strings:
# Apply single profile
ssh +myinstance+%web-server@host
# Apply multiple profiles (later profiles override earlier ones)
ssh +myinstance+%web-server+%development@host
# Combine profiles with direct configuration
ssh +myinstance+%web-server+m8+c4@host # 8GB RAM, 4 CPUs
File includes allow you to inject external file content into configuration values, enabling better organization and reusability of configuration data.
💡 Example: The included
web-server-example
profile demonstrates file includes with<@web-server-init.example.yaml
. See the Quick Start section for a complete working example.
Two syntaxes are supported for file includes:
!include filename.ext
- Standard include directive<@filename.ext
- Alternative include syntaxInclude files are resolved using the following priority:
💡 Real Example: ssh2incus includes a
web-server-example
profile in/etc/ssh2incus/create-config.yaml
that uses<@web-server-init.example.yaml
to deploy a complete nginx web server. See the Quick Start section.
profiles:
cloud-init-example:
image: ubuntu/24.04
config:
# Include cloud-init configuration from external file
user.user-data: !include cloud-init.yaml
web-server:
image: ubuntu/24.04
config:
# Alternative include syntax
user.user-data: <@web-server-init.yaml
# Include shell scripts
user.vendor-data: <@setup-script.sh
database:
image: ubuntu/24.04
config:
# Multiple includes in same profile
user.user-data: !include db-cloud-init.yaml
user.meta-data: <@db-metadata.yaml
cloud-init.yaml:
#cloud-config
package_update: true
package_upgrade: true
packages:
- git
- curl
- wget
users:
- name: admin
passwd: $6$rounds=4096$salt$hash
lock_passwd: false
sudo: ALL=(ALL) NOPASSWD:ALL
ssh_authorized_keys:
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBBMuZwNxXpAyxLXJYtAsSK6cVWp0zTSw+6JulOXn2O9
runcmd:
- touch /tmp/initialization-complete
setup-script.sh:
#!/bin/bash
apt-get update
apt-get install -y nginx
systemctl enable nginx
systemctl start nginx
echo "Web server setup complete" > /var/log/setup.log
ssh2incus supports two instance creation modes:
+
prefix): Remain after SSH session ends~
prefix): Automatically deleted when session ends[~|+][remote:]instance[.project][+configuration]@host
Configuration strings use +
as a separator and support:
Component | Syntax | Example | Description |
---|---|---|---|
Profile | %profile-name |
%web-server |
Apply named profile |
Multiple Profiles | %profile1+%profile2 |
%base+%web-server |
Apply profiles in order |
Image | image/name |
ubuntu/24.04 |
Specify container image |
Memory | m<number> |
m4 |
Memory in GiB |
CPU | c<number> |
c2 |
Number of CPUs |
Disk | d<number> |
d20 |
Disk size in GiB |
Nesting | n or nest |
n |
Enable container nesting |
Privileged | p or priv |
p |
Enable privileged mode |
Ephemeral | e or ephe |
e |
Make instance ephemeral |
# Create persistent instance with defaults
ssh +myinstance@host
# Create ephemeral instance
ssh ~myinstance@host
# Create with specific image
ssh +myinstance+ubuntu/22.04@host
# Create with cloud-init enabled image
ssh +myinstance+ubuntu/24.04/cloud@host
# Create with resource limits
ssh +myinstance+m4+c2+d20@host
# Create with profile
ssh +myinstance+%web-server@host
Configuration values are resolved using the following precedence order (highest to lowest):
create-config.yaml
Given this configuration:
defaults:
image: alpine/edge
memory: 2
cpu: 1
profiles:
web-server:
image: ubuntu/24.04
memory: 4
performance:
memory: 8
cpu: 4
And this SSH command:
ssh +myapp+%web-server+%performance+c2@host
Final configuration will be:
image
: ubuntu/24.04
(from web-server profile)memory
: 8
(from performance profile, overriding web-server)cpu
: 2
(from direct login option, overriding performance profile)💡 Complete Example: ssh2incus includes a full-featured
web-server-example
profile in/etc/ssh2incus/create-config.yaml
with/etc/ssh2incus/web-server-init.example.yaml
demonstrating cloud-init configuration for nginx web server deployment. See the Quick Start section for details.
Incus supports cloud-init configuration through two different sets of configuration options, depending on the image version you're using:
cloud-init.*
configuration optionsuser.*
configuration optionsAs a rule of thumb, newer images support the cloud-init.*
configuration options, while older images support user.*
. However, there might be exceptions to this rule.
Important: To use cloud-init configuration options, you must use images with the /cloud
suffix:
ubuntu/24.04/cloud
(instead of ubuntu/24.04
)alpine/edge/cloud
(instead of alpine/edge
)debian/12/cloud
(instead of debian/12
)Regular images without the /cloud
suffix do not include cloud-init and will ignore these configuration options.
The following configuration options are supported for cloud-init:
Option | Description |
---|---|
cloud-init.vendor-data or user.vendor-data |
General default configuration data |
cloud-init.user-data or user.user-data |
Instance-specific configuration data |
cloud-init.network-config or user.network-config |
Network configuration data |
Both vendor-data and user-data are used to provide cloud configuration data to cloud-init:
Best Practice: Specify vendor-data in profiles and user-data in instance configuration. While Incus allows using both in profiles and instance configurations, following this convention improves maintainability.
When both vendor-data and user-data are supplied for an instance, cloud-init merges the two configurations. However, if you use the same keys in both configurations, merging might not be possible. In such cases, you need to configure how cloud-init should merge the provided data.
For comprehensive cloud-init configuration examples, refer to the official cloud-init documentation:
bootcmd
and runcmd
Ansible
Chef
Puppet
Salt Minion
Using modern cloud-init. options:*
profiles:
modern-web-server:
image: ubuntu/24.04/cloud # /cloud suffix required for cloud-init
config:
# Vendor data for common setup
cloud-init.vendor-data: |
#cloud-config
package_update: true
package_upgrade: true
packages:
- curl
- wget
- htop
# User data for specific configuration
cloud-init.user-data: |
#cloud-config
packages:
- nginx
- certbot
users:
- name: webadmin
sudo: ALL=(ALL) NOPASSWD:ALL
runcmd:
- systemctl enable nginx
- systemctl start nginx
Using legacy user. options:*
profiles:
legacy-server:
image: ubuntu/20.04/cloud # /cloud suffix required for cloud-init
config:
# Vendor data for common setup
user.vendor-data: |
#cloud-config
package_update: true
packages:
- curl
- wget
# User data for specific configuration
user.user-data: |
#cloud-config
packages:
- apache2
users:
- name: admin
sudo: ALL=(ALL) NOPASSWD:ALL
Network configuration example:
profiles:
static-network:
image: ubuntu/24.04/cloud # /cloud suffix required for cloud-init
config:
cloud-init.network-config: |
version: 2
ethernets:
eth0:
dhcp4: false
addresses:
- 192.168.1.100/24
gateway4: 192.168.1.1
nameservers:
addresses: [8.8.8.8, 1.1.1.1]
Using file includes with cloud-init:
profiles:
cloud-init-from-files:
image: ubuntu/24.04/cloud # /cloud suffix required for cloud-init
config:
# Include cloud-init configuration from external files
cloud-init.vendor-data: !include vendor-data.yaml
cloud-init.user-data: <@user-data.yaml
cloud-init.network-config: !include network-config.yaml
Example vendor-data.yaml:
#cloud-config
package_update: true
package_upgrade: true
packages:
- git
- curl
- wget
- vim
- htop
timezone: UTC
locale: en_US.UTF-8
Example user-data.yaml:
#cloud-config
users:
- name: developer
passwd: $6$rounds=4096$salt$hash
lock_passwd: false
sudo: ALL=(ALL) NOPASSWD:ALL
shell: /bin/bash
groups: sudo, docker
ssh_authorized_keys:
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBBMuZwNxXpAyxLXJYtAsSK6cVWp0zTSw+6JulOXn2O9
write_files:
- path: /etc/motd
content: |
Welcome to your development instance!
This instance was configured with cloud-init.
runcmd:
- systemctl enable ssh
- systemctl start ssh
- echo "Instance setup complete" > /var/log/setup.log
To determine whether to use cloud-init.*
or user.*
options:
cloud-init.*
When migrating from older user.*
configuration to newer cloud-init.*
:
# Old configuration (user.*)
profiles:
old-style:
image: ubuntu/20.04/cloud # /cloud suffix required
config:
user.user-data: |
#cloud-config
packages: [nginx]
user.vendor-data: |
#cloud-config
package_update: true
# New configuration (cloud-init.*)
profiles:
new-style:
image: ubuntu/24.04/cloud # /cloud suffix required
config:
cloud-init.user-data: |
#cloud-config
packages: [nginx]
cloud-init.vendor-data: |
#cloud-config
package_update: true
The configuration content remains the same; only the key names change from user.*
to cloud-init.*
.
💡 Ready-to-Use Example: Before diving into these examples, check out the included
web-server-example
profile in/etc/ssh2incus/create-config.yaml
and/etc/ssh2incus/web-server-init.example.yaml
for a complete, working nginx web server deployment. See the Quick Start section for details.
create-config.yaml:
defaults:
image: alpine/edge
ephemeral: false
memory: 2
cpu: 2
disk: 10
vm: false
profiles:
base:
image: ubuntu/24.04/cloud # /cloud suffix required for cloud-init
config:
user.user-data: !include base-cloud-init.yaml
web-frontend:
memory: 4
cpu: 2
disk: 20
config:
user.user-data: !include frontend-setup.yaml
api-backend:
memory: 6
cpu: 3
disk: 30
config:
security.nesting: true
user.user-data: <@backend-setup.yaml
database:
memory: 8
cpu: 4
disk: 50
config:
security.privileged: true
user.user-data: <@database-setup.yaml
Usage:
# Create frontend server
ssh +frontend+%base+%web-frontend@host
# Create API backend with extra resources
ssh +api+%base+%api-backend+m8@host
# Create database server
ssh +db+%base+%database@host
# Quick development instance with common tools
ssh +dev+%development+%cloud-init-tools@host
# Ephemeral testing instance
ssh ~test+%minimal+%testing-tools@host
# High-performance development instance
ssh +workstation+%development+m16+c8+d100@host
profiles:
production:
config:
cloud-init.user-data: !include prod-config.yaml
user.environment.tier: production
staging:
config:
cloud-init.user-data: !include staging-config.yaml
user.environment.tier: staging
nginx-lb:
image: ubuntu/24.04/cloud # /cloud suffix required for cloud-init
memory: 2
config:
user.user-data: <@nginx-lb-setup.yaml
app-server:
image: ubuntu/24.04/cloud # /cloud suffix required for cloud-init
memory: 4
config:
user.user-data: <@app-server-setup.yaml
Deployment:
# Production load balancer
ssh +prod-lb+%production+%nginx-lb@host
# Production app servers
ssh +prod-app1+%production+%app-server@host
ssh +prod-app2+%production+%app-server@host
# Staging environment
ssh +staging-lb+%staging+%nginx-lb+m1@host
ssh +staging-app+%staging+%app-server+m2@host
You can use file includes to inject complex setup scripts:
profiles:
kubernetes-node:
image: ubuntu/24.04/cloud # /cloud suffix required for cloud-init
memory: 8
cpu: 4
config:
security.nesting: true
security.privileged: true
user.user-data: |
#cloud-config
write_files:
- path: /opt/k8s-setup.sh
permissions: '0755'
content: |
#!/bin/bash
echo "Starting Kubernetes setup..."
# Add more setup commands here
echo "Kubernetes setup complete!"
runcmd:
- /opt/k8s-setup.sh
Use multiple profiles for conditional setups:
profiles:
base-ubuntu:
image: ubuntu/24.04
base-alpine:
image: alpine/edge
gpu-support:
devices:
gpu0:
type: gpu
storage-optimized:
devices:
storage:
type: disk
size: 100GB
Usage:
# GPU-enabled Ubuntu instance
ssh +gpu-workstation+%base-ubuntu+%gpu-support@host
# Storage-optimized Alpine instance
ssh +storage-server+%base-alpine+%storage-optimized@host
Create profile hierarchies:
profiles:
base:
image: ubuntu/24.04/cloud # /cloud suffix required for cloud-init
config:
user.user-data: !include base-setup.yaml
web-base:
memory: 4
config:
user.user-data: !include web-common.yaml
nginx-server:
config:
user.user-data: <@nginx-specific.yaml
apache-server:
config:
user.user-data: <@apache-specific.yaml
Usage:
# Nginx web server with base configuration
ssh +web1+%base+%web-base+%nginx-server@host
# Apache web server with base configuration
ssh +web2+%base+%web-base+%apache-server@host
File Include Not Found
Error: could not read include file /path/to/file.yaml: no such file or directory
Profile Not Found
Error: profile "web-server" does not exist
create-config.yaml
profiles:
sectionCloud-Init Not Working
Error: cloud-init configuration ignored or not applied
/cloud
suffix (e.g., ubuntu/24.04/cloud
, alpine/edge/cloud
)/cloud
suffix don't include cloud-initcloud-init.*
or user.*
configuration optionssystemctl status cloud-init
Invalid Configuration Syntax
Error: yaml: unmarshal errors
Permission Issues
Error: permission denied reading config file
Test Configuration Loading
# Test configuration parsing
ssh2incus --dump-create
Verbose Logging
# Enable debug logging
ssh2incus --debug
File Organization
Profile Design
Security Considerations
Testing
ssh2incus looks for configuration files in the following order:
./create-config.yaml
~/.config/ssh2incus/create-config.yaml
/etc/ssh2incus/create-config.yaml
The first found configuration file is used. Include files are resolved relative to the directory containing the configuration file that references them.