Configuring Hyper is done through four layers, in priority:
Configuration Files
| File | Description |
|---|---|
/etc/hyper/config.exs | The 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.toml | The /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.exs | The 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. |
| Defaults | Hyper 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.exsrefers to/etc/hyper/config.exs.config.tomlrefers to/etc/hyper/config.toml.- All keys under
config.exsare written in short-hand form. The parent group is given as the section title. For example,.mke2fsin the tool configuration section expands to:hyper, Hyper.Cfg.Tools, mke2fs: "/path/to/mke2fs".
Root Keys
| Config Key | config.exs | config.toml | Default | Notes |
|---|---|---|---|---|
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 Key | config.exs | config.toml | Default | Notes |
|---|---|---|---|---|
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 | .umoci | Automatically 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 Key | config.exs | config.toml | Default | Notes |
|---|---|---|---|---|
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 Key | config.exs | config.toml | Default | Notes |
|---|---|---|---|---|
enabled | .enabled | .enabled | false | |
port | .port | .port | 50051 | The port on which to serve the interface. |
cred | .cred | .cred | nil | Either 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 Key | config.exs | config.toml | Default | Notes |
|---|---|---|---|---|
proto | .proto | .proto | http_protobuf | One 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 Key | config.exs | config.toml | Default | Notes |
|---|---|---|---|---|
mem_max | .mem_max | .mem_max | 4 GiB | $\alpha$ (hard) budget: total unit of RAM the node offers VMs. Must not exceed available system memory. |
disk_max | .disk_max | .disk_max | 4 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_load | 0.8 | $\beta$ (soft) budget: instantaneous CPU load threshold, a fraction 0.0–1.0 of the cap. |
cpu_max_cap | .cpu_max_cap | .cpu_max_cap | 4.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_cap | 1 GiBps | $\beta$ (soft) budget: soft disk-bandwidth capacity (unit). |
disk_bw_max_load | .disk_bw_max_load | .disk_bw_max_load | 0.8 | $\beta$ (soft) budget: disk-bandwidth load threshold, a fraction 0.0–1.0 of the cap. |
net_bw_cap | .net_bw_cap | .net_bw_cap | 1 GiBps | $\beta$ (soft) budget: soft network-bandwidth capacity (unit). |
net_bw_max_load | .net_bw_max_load | .net_bw_max_load | 0.8 | $\beta$ (soft) budget: network-bandwidth load threshold, a fraction 0.0–1.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.8config.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.8VmLinux Paths
Hyper requires Linux images for the architectures it runs on:
| Config Key | config.exs | config.toml | Default | Notes |
|---|---|---|---|---|
amd64 | .amd64 | .amd64 | Automatically downloaded from hyper-vmlinux. | Absolute Path. |
aarch64 | .aarch64 | .aarch64 | Automatically 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 Key | config.exs | config.toml | Default | Notes |
|---|---|---|---|---|
store | .store | .store | <work_dir>/layers | Absolute 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 Key | config.exs | config.toml | Default | Notes |
|---|---|---|---|---|
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 Key | config.exs | config.toml | Default | Notes |
|---|---|---|---|---|
batch_size | .batch_size | .batch_size | 200 | |
batch_pause | .batch_pause | .batch_pause | 100ms | |
sweep_interval | .sweep_interval | .sweep_interval | 60s | |
acquire_interval | .acquire_interval | .acquire_interval | 5s | |
retry | .retry | .retry | 60s | |
timeout | .timeout | .timeout | 5s | |
grace_period | .grace_period | .grace_period | 1h |
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 Key | config.exs | config.toml | Default | Notes |
|---|---|---|---|---|
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.
firecrackermust 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"