Getting started

LXD installation details

At the time of writing this role (December 2019) LXD was not available natively in Debian. Packaging efforts are ongoing, however there's no telling if LXD will be included in the next Debian release (Bullseye). The upstream developers suggest installation on Debian via Snap, however this brings its own set of issues which are offtopic here.

Instead, on Debian hosts the debops.lxd role utilizes the debops.golang role to install the lxd and lxc binaries from upstream git repository by compiling them from source. The role will configure the rest of the needed infrastructure (systemd unit files, logrotate and sysctl configuration, POSIX groups, log directory, etc.) so that the LXD service should work out of the box on Debian without the need of a Snap installation.

The debops.golang configuration for building and installing LXD is defined in the lxd__golang__dependent_packages variable.

Due to the build dependency on the lxc-dev APT package, which pulls the lxc APT package automatically, the debops.lxc role and its dependencies will be used to configure the LXC environment. The lxcbr0 network bridge will be automatically disabled in this case.


Merge commits in the lxc/lxd GitHub repository might be signed with the GPG key issued by GitHub, used for signing commits done in the web interface. It has to be done, because tagged LXD releases have problems with their dependency chains and due to that the debops.lxd role relies on stable branches in the LXD repository. The trust is limited to the _golang UNIX account and might have an impact for any Go applications built in that specific environment.

Example inventory

To enable LXD support on a host, it needs to be added to the [debops_service_lxd] Ansible inventory group:





By default, containers will use the lxdbr0 bridge managed by the LXD service, with their own internal lxd subdomain. You can use the debops.ifupdown Ansible role to configure additional network bridges on the LXD host, if you want to attach the containers to the public network.

Example playbook

If you are using this role without DebOps, here's an example Ansible playbook that uses the debops.lxd role:


- name: Manage LXD service
  collections: [ 'debops.debops', 'debops.roles01',
                 'debops.roles02', 'debops.roles03' ]
  hosts: [ 'debops_service_lxd' ]
  become: True

  environment: '{{ inventory__environment | d({})
                   | combine(inventory__group_environment | d({}))
                   | combine(inventory__host_environment  | d({})) }}'


    - role: resolvconf
      tags: [ 'role::resolvconf', 'skip::resolvconf' ]
      resolvconf__enabled: True

    - role: root_account
      tags: [ 'role::root_account', 'skip::root_account' ]

    - role: keyring
      tags: [ 'role::keyring', 'skip::keyring', 'role::golang' ]
      keyring__dependent_gpg_user: '{{ golang__keyring__dependent_gpg_user }}'
        - '{{ golang__keyring__dependent_gpg_keys }}'
      golang__dependent_packages:  # noqa var-naming[no-role-prefix]
        - '{{ lxd__golang__dependent_packages }}'

    - role: apt_preferences
      tags: [ 'role::apt_preferences', 'skip::apt_preferences' ]
        - '{{ golang__apt_preferences__dependent_list }}'
        - '{{ lxc__apt_preferences__dependent_list }}'

    - role: golang
      tags: [ 'role::golang', 'skip::golang' ]
        - '{{ lxd__golang__dependent_packages }}'

    - role: cron
      tags: [ 'role::cron', 'skip::cron' ]

    - role: logrotate
      tags: [ 'role::logrotate', 'skip::logrotate' ]
        - '{{ lxd__logrotate__dependent_config }}'

    - role: ferm
      tags: [ 'role::ferm', 'skip::ferm' ]
        - '{{ lxc__ferm__dependent_rules }}'

    - role: python
      tags: [ 'role::python', 'skip::python', 'role::lxc' ]
        - '{{ lxc__python__dependent_packages3 }}'
        - '{{ lxc__python__dependent_packages2 }}'

    - role: sysctl
      tags: [ 'role::sysctl', 'skip::sysctl' ]
        - '{{ lxc__sysctl__dependent_parameters }}'
        - '{{ lxd__sysctl__dependent_parameters }}'

    - role: lxc
      tags: [ 'role::lxc', 'skip::lxc' ]

    - role: lxd
      tags: [ 'role::lxd', 'skip::lxd' ]

# If a host has 'debops.dnsmasq' or 'debops.unbound' roles configured, execute
# its playbook in case that configuration applied by the 'lxd' role needs to be
# applied to 'dnsmasq' or 'unbound' services. This should ensure that the
# '*.lxd' subdomain for internal LXD containers is resolvable on the LXD host.
# If the host is not in the Ansible inventory groups required by the
# 'dnsmasq.yml' or the 'unbound.yml' playbooks, this should not impact
# anything.

- name: Configure dnsmasq service
  import_playbook: 'dnsmasq.yml'

- name: Configure unbound service
  import_playbook: 'unbound.yml'

Ansible tags

You can use Ansible --tags or --skip-tags parameters to limit what tasks are performed during Ansible run. This can be used after a host was first configured to speed up playbook execution, when you are sure that most of the configuration is already in the desired state.

Available role tags:


Main role tag, should be used in the playbook to execute all of the role tasks as well as role dependencies.


Re-apply the LXD preseeding configuration. Requires the lxd__init_preseed variable to be set to True to be effective.

Other resources

List of other useful resources related to the debops.lxd Ansible role: