Configuring Hyper is done through four layers, in priority:

Configuration Files

FileDescription
/etc/hyper/config.exsThe config.exs file is exclusively used by the unprivileged hyper application. The purpose of this file is to allow you to load configuration values at runtime. If you are using a secrets manager, this is the right place to load the secrets. Must be owned by root and only writeable by root.
/etc/hyper/config.tomlThe /etc/hyper/config.toml file is used for static configuration. Unlike config.exs, it is used by both Hyper and hyper-suidhelper which means that it can impact the behavior of a process running under root. Must be owned by root and only writable by root.
Compile-Time config.exsThe compile-time configuration is generally used to fine-tune the performance of Hyper. You likely do not need to edit most of the configuration fields exposed by this file for day-to-day usage, but they are available for you to tweak.
DefaultsHyper has a set of sane defaults for some, but not all config fields.

Note that not all layers allow all configuration fields to be tweaked. Read further for more details on where and how each configuration field is set.

Configuration Fields

This section briefly outlines the configuration fields available in Hyper. Note the keys are abbreviated for better layout:

  • config.exs refers to /etc/hyper/config.exs.
  • config.toml refers to /etc/hyper/config.toml.
  • All keys under config.exs are written in short-hand form. The parent group is given as the section title. For example, .mke2fs in the tool configuration section expands to :hyper, Hyper.Cfg.Tools, mke2fs: "/path/to/mke2fs".

Root Keys

Config Keyconfig.exsconfig.tomlDefaultNotes
work_dir-work_dir"/srv/hyper"Absolute Path where Hyper creates its working tree.

config.toml

work_dir = "/srv/hyper"

Tool Configuration

Hyper relies on a large number of external tools, of which the paths are configurable:

Config Keyconfig.exsconfig.tomlDefaultNotes
firecracker-.firecracker-Safe Path
jailer-.jailer-Safe Path
dmsetup-.dmsetup"/usr/sbin/dmsetup"Safe Path
losetup-.losetup"/usr/sbin/losetup"Safe Path
blockdev-.blockdev"/usr/sbin/blockdev"Safe Path
thin_dump-.thin_dump"/usr/sbin/thin_dump"Safe Path. Reads thin snapshot metadata for fork publish; required for Hyper.Vm.fork/1's cross-node path (same-node fast_fork/1 is unaffected).
mke2fs.mke2fs.mke2fs$PATH["mke2fs"]
skopeo.skopeo.skopeo$PATH["skopeo"]
umoci.umoci.umociAutomatically downloaded.
suidhelper.suidhelper.suidhelper"/usr/local/bin/hyper-suidhelper"Absolute Path

config.exs

# Note that not all the tools are available here. Privileged tools, used by the
# suidhelper, cannot be configured here.
config :hyper, Hyper.Cfg.Tools,
  skopeo: "/usr/local/bin/skopeo",
  mke2fs: "/usr/local/bin/mke2fs",
  umoci: "/usr/local/bin/umoci",
  suidhelper: "/usr/local/bin/hyper-suidhelper"

config.toml

[tools]
# Privileged tools -- config.toml only (shared with the suidhelper).
firecracker = "/opt/firecracker/firecracker"
jailer      = "/opt/firecracker/jailer"
dmsetup     = "/usr/sbin/dmsetup"
losetup     = "/usr/sbin/losetup"
blockdev    = "/usr/sbin/blockdev"
thin_dump   = "/usr/sbin/thin_dump"

# Node tools -- may also be set in config.exs, which wins when both are set.
skopeo     = "/usr/local/bin/skopeo"
mke2fs     = "/usr/local/bin/mke2fs"
umoci      = "/usr/local/bin/umoci"
suidhelper = "/usr/local/bin/hyper-suidhelper"

Jail Confinement

Config Keyconfig.exsconfig.tomlDefaultNotes
cgroup-.cgroup"hyper"Parent cgroup under which each VM's cgroup is nested. Each VM receives its own ephemeral cgroup which lives under the umbrella of this cgroup.
uid_gid_range-.uid_gid_range-Required. Range limiting the UID/GID values given to VMs. Each VM receives its own UID/GID pair, within these bounds. Must not be an existing user/group.

config.toml

[jails]
cgroup = "hyper"
uid_gid_range = [900000, 999999]

VM Networking Configuration

Required. Every VM gets NAT'd egress out through a physical host interface, and a node refuses to start without a [network] table (see the install guide for the host prerequisites: iproute2/nftables and net.ipv4.ip_forward=1). config.toml-only — the setuid helper reads the same uplink and clone_pool to build each VM's netns, veth pair, and NAT rules, so it cannot live in config.exs.

config.toml

[network]
# **required**. The physical uplink interface guest egress is NAT'd out through.
# On a single-uplink host, the default-route NIC is the usual choice:
#   ip route show default | awk '{print $5; exit}'
uplink = "eth0"

# optional -- defaults shown. The IPv4 CIDR the per-VM /30s are carved from.
clone_pool = "172.31.0.0/16"

# optional -- defaults shown. DNS resolver handed to guests via the kernel
# cmdline (the guest agent writes it to /etc/resolv.conf).
resolver = "1.1.1.1"

gRPC Configuration

Hyper supports a gRPC interface enabling you to interface with Hyper from any language.

Config Keyconfig.exsconfig.tomlDefaultNotes
enabled.enabled.enabledfalse
port.port.port50051The port on which to serve the interface.
cred.cred.crednilEither a GRPC.Credential or a TOML struct { cert = "/path/to/cert.pem", key = "/path/to/key.pem"}. Cleartext mode when nil.
adapter_opts.adapter_opts.adapter_opts[]Options forwarded to the gRPC server adapter, e.g. the bind address ip: {0, 0, 0, 0}.

Uniqueness

Note that if you choose to use a homogenous configuration across all your nodes and you enable the gRPC server on all of them, you will spawn multiple gRPC servers, one-per-node, in your cluster.

This is perfectly legal, if you so desire, but it is important to note that you can also conditionally enable the gRPC server based on logic in your config.exs, for example, to only spawn it on your "main" server.

config.exs

config :hyper, Hyper.Cfg.Grpc,
  enabled: true,
  port: 50_051,
  cred:
    GRPC.Credential.new(
      ssl: [certfile: "/etc/hyper/tls/cert.pem", keyfile: "/etc/hyper/tls/key.pem"]
    )

config.toml

[grpc]
enabled = true
port = 50051
cred = { cert = "/etc/hyper/tls/cert.pem", key = "/etc/hyper/tls/key.pem" }

Telemetry Configuration

You can configure telemetry with Hyper by adding this section to your configuration and Hyper will emit tracing spans as configured.

Config Keyconfig.exsconfig.tomlDefaultNotes
proto.proto.protohttp_protobufOne of http_protobuf or grpc.
endpoint.endpoint.endpoint-OTLP collector URL. Falls back to the standard OTEL_EXPORTER_OTLP_ENDPOINT environment variable; telemetry is disabled when none is set.
headers.headers.headers-Headers sent with each export, e.g. an auth token for your backend.

config.exs

config :hyper, Hyper.Cfg.Otel,
  proto: :http_protobuf,
  endpoint: "https://otel-collector.internal:4318",
  headers: %{"authorization" => "Bearer YOUR_TOKEN"}

config.toml

[otel]
proto = "http_protobuf"
endpoint = "https://otel-collector.internal:4318"
headers = { "authorization" = "Bearer YOUR_TOKEN" }

Budget Configuration

Hyper allows you to control the absolute maximal budgets that are available to all VMs on a particular node. Every field has a built-in default; set only the keys you need to change.

Config Keyconfig.exsconfig.tomlDefaultNotes
mem_max.mem_max.mem_max4 GiB$\alpha$ (hard) budget: total unit of RAM the node offers VMs. Must not exceed available system memory.
disk_max.disk_max.disk_max4 GiB$\alpha$ (hard) budget: total unit of disk the node offers VMs. Must not exceed available system disk.
cpu_max_load.cpu_max_load.cpu_max_load0.8$\beta$ (soft) budget: instantaneous CPU load threshold, a fraction 0.01.0 of the cap.
cpu_max_cap.cpu_max_cap.cpu_max_cap4.0$\beta$ (soft) budget: soft CPU capacity in cores (a float). Optional — an explicit nil in config.exs disables the cap.
disk_bw_cap.disk_bw_cap.disk_bw_cap1 GiBps$\beta$ (soft) budget: soft disk-bandwidth capacity (unit).
disk_bw_max_load.disk_bw_max_load.disk_bw_max_load0.8$\beta$ (soft) budget: disk-bandwidth load threshold, a fraction 0.01.0 of the cap.
net_bw_cap.net_bw_cap.net_bw_cap1 GiBps$\beta$ (soft) budget: soft network-bandwidth capacity (unit).
net_bw_max_load.net_bw_max_load.net_bw_max_load0.8$\beta$ (soft) budget: network-bandwidth load threshold, a fraction 0.01.0 of the cap.

config.exs

config :hyper, Hyper.Cfg.Budget,
  mem_max: Unit.Information.gib(4),
  disk_max: Unit.Information.gib(64),
  cpu_max_load: 0.8,
  cpu_max_cap: 4.0,
  disk_bw_cap: Unit.Bandwidth.gibps(1),
  disk_bw_max_load: 0.8,
  net_bw_cap: Unit.Bandwidth.gibps(1),
  net_bw_max_load: 0.8

config.toml

[budget]
mem_max = "4GiB"
disk_max = "64GiB"
cpu_max_load = 0.8
cpu_max_cap = 4.0
disk_bw_cap = "1GiBps"
disk_bw_max_load = 0.8
net_bw_cap = "1GiBps"
net_bw_max_load = 0.8

VmLinux Paths

Hyper requires Linux images for the architectures it runs on:

Config Keyconfig.exsconfig.tomlDefaultNotes
amd64.amd64.amd64Automatically downloaded from hyper-vmlinux.Absolute Path.
aarch64.aarch64.aarch64Automatically downloaded from hyper-vmlinux.Absolute Path.

config.exs

config :hyper, Hyper.Cfg.VmLinux,
  amd64: "/opt/hyper/kernels/vmlinux-amd64",
  aarch64: "/opt/hyper/kernels/vmlinux-aarch64"

config.toml

[vmlinux]
amd64 = "/opt/hyper/kernels/vmlinux-amd64"
aarch64 = "/opt/hyper/kernels/vmlinux-aarch64"

Image Configuration

Hyper's image provisioning layer stores read-only layers on a configurable medium. (The device-mapper geometry behind it is fixed at compile time and rarely needs tweaking.)

Config Keyconfig.exsconfig.tomlDefaultNotes
store.store.store<work_dir>/layersAbsolute Path to the layer storage medium.

config.exs

config :hyper, Hyper.Cfg.Img,
  store: "/mnt/hyper/layers"

config.toml

[img]
store = "/mnt/hyper/layers"

Additionally, sub-sections are available.

Database Configuration

These are secrets, so they are config.exs-only. Any key you set overrides the image-database repo's compiled-in connection config; keys you omit keep that built-in value.

Config Keyconfig.exsconfig.tomlDefaultNotes
database.database--Postgres database name.
username.username--Postgres role.
password.password--Postgres password — load it from a secrets manager.
hostname.hostname--Postgres host.

config.exs

config :hyper, Hyper.Cfg.Img.Db,
  database: "hyper",
  username: "hyper",
  password: System.fetch_env!("HYPER_DB_PASSWORD"),
  hostname: "db.internal"

Garbage Collector Configuration

Hyper supports a mechanism to prune unreferenced image layers. Unreferenced image layers occur when an ungraceful crash happens, resulting in entries in the layer medium which are not referenced by the database, and, consequently, unusable. This is always enabled. Since this scans through the whole layer database, it can have an impact on performance, and tweaking it may be necessary.

Config Keyconfig.exsconfig.tomlDefaultNotes
batch_size.batch_size.batch_size200
batch_pause.batch_pause.batch_pause100ms
sweep_interval.sweep_interval.sweep_interval60s
acquire_interval.acquire_interval.acquire_interval5s
retry.retry.retry60s
timeout.timeout.timeout5s
grace_period.grace_period.grace_period1h

config.exs

config :hyper, Hyper.Cfg.Img.Gc,
  batch_size: 200,
  batch_pause: Unit.Time.ms(100),
  sweep_interval: Unit.Time.s(60),
  acquire_interval: Unit.Time.s(5),
  retry: Unit.Time.s(60),
  timeout: Unit.Time.s(5),
  grace_period: Unit.Time.s(3600)

config.toml

[img.gc]
batch_size = 200
batch_pause = "100ms"
sweep_interval = "60s"
acquire_interval = "5s"
retry = "60s"
timeout = "5s"
grace_period = "1h"

Cluster Topology

Hyper forms a BEAM cluster (Distributed Erlang) so nodes discover each other and share VM routing and budget state. The topology is given in libcluster syntax and forwarded directly to it. Because a topology references strategy modules, it is config.exs-only. Omit it — the default — to run a single, unclustered node.

Config Keyconfig.exsconfig.tomlDefaultNotes
topologies.topologies-[]A libcluster topology keyword list. [] is single-node.

config.exs

config :hyper, Hyper.Cfg.Cluster,
  topologies: [
    hyper: [
      strategy: Cluster.Strategy.DNSPoll,
      config: [query: "hyper.svc.cluster.local", node_basename: "hyper"]
    ]
  ]

config.toml

# Cluster topologies reference strategy modules, so they are set in config.exs only.

Key Types

Absolute Path

  • Typed as a string in TOML and elixir.
  • Must be given as an absolute path.

Safe Path

  • Is an Absolute Path.
  • The basename must match the configuration, eg. firecracker must have a path /foo/bar/firecracker.
  • Must be owned by root.
  • Must be only writable by root.
  • Must not be a symlink.

Range

  • Typed as a 2-tuple in Elixir
  • Typed as an array of two elements in TOML.
  • [min, max] semantics.

Unit

  • Represents a unit as defined in Unit.*.

Total Examples

A complete config.exs and config.toml exercising every section:

config.exs

import Config

config :hyper, Hyper.Cfg.Cluster,
  topologies: [
    hyper: [
      strategy: Cluster.Strategy.DNSPoll,
      config: [query: "hyper.svc.cluster.local", node_basename: "hyper"]
    ]
  ]

# Node-only tools (config.exs wins over config.toml for these).
config :hyper, Hyper.Cfg.Tools,
  skopeo: "/usr/local/bin/skopeo",
  mke2fs: "/usr/local/bin/mke2fs"

config :hyper, Hyper.Cfg.Grpc,
  enabled: true,
  port: 50_051

config :hyper, Hyper.Cfg.Otel,
  proto: :http_protobuf,
  endpoint: "https://otel-collector.internal:4318",
  headers: %{"authorization" => System.fetch_env!("OTEL_AUTH_TOKEN")}

config :hyper, Hyper.Cfg.Budget,
  mem_max: Unit.Information.gib(4),
  disk_max: Unit.Information.gib(64),
  cpu_max_load: 0.8,
  cpu_max_cap: 4.0,
  disk_bw_cap: Unit.Bandwidth.gibps(1),
  disk_bw_max_load: 0.8,
  net_bw_cap: Unit.Bandwidth.gibps(1),
  net_bw_max_load: 0.8

config :hyper, Hyper.Cfg.Img,
  store: "/mnt/hyper/layers"

config :hyper, Hyper.Cfg.Img.Db,
  database: "hyper",
  username: "hyper",
  password: System.fetch_env!("HYPER_DB_PASSWORD"),
  hostname: "db.internal"

config.toml

work_dir = "/srv/hyper"

[tools]
firecracker = "/opt/firecracker/firecracker"
jailer      = "/opt/firecracker/jailer"

[jails]
cgroup = "hyper"
uid_gid_range = [900000, 999999]

[grpc]
enabled = true
port = 50051

[otel]
proto = "http_protobuf"
endpoint = "https://otel-collector.internal:4318"

[budget]
mem_max = "4GiB"
disk_max = "64GiB"
cpu_max_load = 0.8
cpu_max_cap = 4.0
disk_bw_cap = "1GiBps"
disk_bw_max_load = 0.8
net_bw_cap = "1GiBps"
net_bw_max_load = 0.8

[vmlinux]
amd64 = "/opt/hyper/kernels/vmlinux-amd64"
aarch64 = "/opt/hyper/kernels/vmlinux-aarch64"

[img]
store = "/mnt/hyper/layers"

[img.gc]
batch_size = 200
sweep_interval = "60s"
grace_period = "1h"