Skip to content

Commit 6082ec9

Browse files
pranav767shanduur
authored andcommitted
feat: add support for tmpfs based EPHEMERAL volumes
Signed-off-by: Pranav Patil <pranavppatil767@gmail.com> Signed-off-by: Mateusz Urbanek <mateusz.urbanek@siderolabs.com>
1 parent c3f6f35 commit 6082ec9

40 files changed

Lines changed: 1118 additions & 156 deletions

.github/workflows/ci.yaml

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
22
#
3-
# Generated on 2026-05-11T14:12:46Z by kres 1762ab2.
3+
# Generated on 2026-05-15T14:40:33Z by kres 1762ab2.
44

55
concurrency:
66
group: ${{ github.head_ref || github.run_id }}
@@ -4598,6 +4598,83 @@ jobs:
45984598
/tmp/logs-*.tar.gz
45994599
/tmp/support-*.zip
46004600
retention-days: "5"
4601+
integration-qemu-ephemeral:
4602+
permissions:
4603+
actions: read
4604+
contents: write
4605+
issues: read
4606+
packages: write
4607+
pull-requests: read
4608+
runs-on:
4609+
group: large
4610+
if: contains(fromJSON(needs.default.outputs.labels || '[]'), 'integration/qemu-ephemeral') || contains(fromJSON(needs.default.outputs.labels || '[]'), 'integration/release-gate')
4611+
needs:
4612+
- default
4613+
steps:
4614+
- name: gather-system-info
4615+
id: system-info
4616+
uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0
4617+
continue-on-error: true
4618+
- name: print-system-info
4619+
run: |
4620+
MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024))
4621+
4622+
OUTPUTS=(
4623+
"CPU Core: ${{ steps.system-info.outputs.cpu-core }}"
4624+
"CPU Model: ${{ steps.system-info.outputs.cpu-model }}"
4625+
"Hostname: ${{ steps.system-info.outputs.hostname }}"
4626+
"NodeName: ${NODE_NAME}"
4627+
"Kernel release: ${{ steps.system-info.outputs.kernel-release }}"
4628+
"Kernel version: ${{ steps.system-info.outputs.kernel-version }}"
4629+
"Name: ${{ steps.system-info.outputs.name }}"
4630+
"Platform: ${{ steps.system-info.outputs.platform }}"
4631+
"Release: ${{ steps.system-info.outputs.release }}"
4632+
"Total memory: ${MEMORY_GB} GB"
4633+
)
4634+
4635+
for OUTPUT in "${OUTPUTS[@]}";do
4636+
echo "${OUTPUT}"
4637+
done
4638+
continue-on-error: true
4639+
- name: checkout
4640+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2
4641+
- name: Unshallow
4642+
run: |
4643+
git fetch --prune --unshallow
4644+
- name: Set up Docker Buildx
4645+
id: setup-buildx
4646+
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0
4647+
with:
4648+
driver: remote
4649+
endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234
4650+
timeout-minutes: 10
4651+
- name: Download artifacts
4652+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # version: v8.0.1
4653+
with:
4654+
name: talos-artifacts
4655+
path: _out
4656+
- name: Fix artifact permissions
4657+
run: |
4658+
xargs -a _out/executable-artifacts -I {} chmod +x {}
4659+
- name: ci-temp-release-tag
4660+
run: |
4661+
make ci-temp-release-tag
4662+
- name: e2e-qemu
4663+
env:
4664+
IMAGE_REGISTRY: registry.dev.siderolabs.io
4665+
QEMU_MEMORY_CONTROLPLANES: "6144"
4666+
WITH_EPHEMERAL_NODE: "true"
4667+
run: |
4668+
sudo -E make e2e-qemu
4669+
- name: save artifacts
4670+
if: always()
4671+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # version: v7.0.1
4672+
with:
4673+
name: talos-logs-integration-qemu-ephemeral
4674+
path: |-
4675+
/tmp/logs-*.tar.gz
4676+
/tmp/support-*.zip
4677+
retention-days: "5"
46014678
integration-qemu-race:
46024679
permissions:
46034680
actions: read
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
2+
#
3+
# Generated on 2026-05-15T14:40:33Z by kres 1762ab2.
4+
5+
concurrency:
6+
group: ${{ github.head_ref || github.run_id }}
7+
cancel-in-progress: true
8+
"on":
9+
workflow_run:
10+
workflows:
11+
- default
12+
types:
13+
- completed
14+
name: integration-qemu-ephemeral-triggered
15+
jobs:
16+
default:
17+
permissions:
18+
actions: read
19+
runs-on:
20+
group: large
21+
if: github.event.workflow_run.conclusion == 'success'
22+
steps:
23+
- name: gather-system-info
24+
id: system-info
25+
uses: kenchan0130/actions-system-info@59699597e84e80085a750998045983daa49274c4 # version: v1.4.0
26+
continue-on-error: true
27+
- name: print-system-info
28+
run: |
29+
MEMORY_GB=$((${{ steps.system-info.outputs.totalmem }}/1024/1024/1024))
30+
31+
OUTPUTS=(
32+
"CPU Core: ${{ steps.system-info.outputs.cpu-core }}"
33+
"CPU Model: ${{ steps.system-info.outputs.cpu-model }}"
34+
"Hostname: ${{ steps.system-info.outputs.hostname }}"
35+
"NodeName: ${NODE_NAME}"
36+
"Kernel release: ${{ steps.system-info.outputs.kernel-release }}"
37+
"Kernel version: ${{ steps.system-info.outputs.kernel-version }}"
38+
"Name: ${{ steps.system-info.outputs.name }}"
39+
"Platform: ${{ steps.system-info.outputs.platform }}"
40+
"Release: ${{ steps.system-info.outputs.release }}"
41+
"Total memory: ${MEMORY_GB} GB"
42+
)
43+
44+
for OUTPUT in "${OUTPUTS[@]}";do
45+
echo "${OUTPUT}"
46+
done
47+
continue-on-error: true
48+
- name: checkout
49+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # version: v6.0.2
50+
- name: Unshallow
51+
run: |
52+
git fetch --prune --unshallow
53+
- name: Set up Docker Buildx
54+
id: setup-buildx
55+
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # version: v4.0.0
56+
with:
57+
driver: remote
58+
endpoint: tcp://buildkit-amd64.ci.svc.cluster.local:1234
59+
timeout-minutes: 10
60+
- name: Download artifacts
61+
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # version: v8.0.1
62+
with:
63+
github-token: ${{ secrets.GITHUB_TOKEN }}
64+
name: talos-artifacts
65+
path: _out
66+
run-id: ${{ github.event.workflow_run.id }}
67+
- name: Fix artifact permissions
68+
run: |
69+
xargs -a _out/executable-artifacts -I {} chmod +x {}
70+
- name: ci-temp-release-tag
71+
run: |
72+
make ci-temp-release-tag
73+
- name: e2e-qemu
74+
env:
75+
IMAGE_REGISTRY: registry.dev.siderolabs.io
76+
QEMU_MEMORY_CONTROLPLANES: "6144"
77+
WITH_EPHEMERAL_NODE: "true"
78+
run: |
79+
sudo -E make e2e-qemu
80+
- name: save artifacts
81+
if: always()
82+
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # version: v7.0.1
83+
with:
84+
name: talos-logs-integration-qemu-ephemeral
85+
path: |-
86+
/tmp/logs-*.tar.gz
87+
/tmp/support-*.zip
88+
retention-days: "5"

.github/workflows/slack-notify-ci-failure.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
22
#
3-
# Generated on 2026-04-27T17:59:00Z by kres e4dc583.
3+
# Generated on 2026-05-15T14:40:33Z by kres 1762ab2.
44

55
"on":
66
workflow_run:
@@ -9,6 +9,7 @@
99
- grype-scan-cron
1010
- artifacts-cron
1111
- integration-qemu-triggered
12+
- integration-qemu-ephemeral-triggered
1213
- integration-embedded-triggered
1314
- integration-conformance-triggered
1415
- integration-trusted-boot-triggered

.github/workflows/slack-notify.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# THIS FILE WAS AUTOMATICALLY GENERATED, PLEASE DO NOT EDIT.
22
#
3-
# Generated on 2026-04-27T17:59:00Z by kres e4dc583.
3+
# Generated on 2026-05-15T14:40:33Z by kres 1762ab2.
44

55
"on":
66
workflow_run:
@@ -9,6 +9,7 @@
99
- grype-scan-cron
1010
- artifacts-cron
1111
- integration-qemu-triggered
12+
- integration-qemu-ephemeral-triggered
1213
- integration-embedded-triggered
1314
- integration-conformance-triggered
1415
- integration-trusted-boot-triggered

.kres.yaml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ spec:
9393
integration/qemu-default: "Run QEMU integration tests on default variant"
9494
integration/qemu-encrypted-vip: "Run QEMU integration tests with disk encryption and virtual IP"
9595
integration/qemu-enforcing: "Run QEMU integration tests on enforcing variant"
96+
integration/qemu-ephemeral: "Run QEMU integration tests for a fully ephemeral single-node cluster"
9697
integration/qemu-race: "Run QEMU integration tests with the race detector"
9798
integration/release-gate: "Run all release-gate integration tests"
9899
integration/reproducibility-test: "Run build reproducibility tests"
@@ -527,6 +528,41 @@ spec:
527528
artifactPath: /tmp/logs-*.tar.gz
528529
additionalArtifacts:
529530
- "/tmp/support-*.zip"
531+
- name: integration-qemu-ephemeral
532+
buildxOptions:
533+
enabled: true
534+
depends:
535+
- default
536+
runnerGroup: large
537+
onWorkflowRun:
538+
workflows: [default]
539+
types: [completed]
540+
triggerLabels:
541+
- integration/qemu-ephemeral
542+
- integration/release-gate
543+
steps:
544+
- name: download-artifacts
545+
artifactStep:
546+
type: download
547+
artifactName: talos-artifacts
548+
artifactPath: _out
549+
- name: ci-temp-release-tag
550+
- name: e2e-qemu
551+
withSudo: true
552+
environment:
553+
IMAGE_REGISTRY: registry.dev.siderolabs.io
554+
WITH_EPHEMERAL_NODE: "true"
555+
QEMU_MEMORY_CONTROLPLANES: "6144"
556+
- name: save-talos-logs
557+
conditions:
558+
- always
559+
artifactStep:
560+
type: upload
561+
artifactName: talos-logs-integration-qemu-ephemeral
562+
disableExecutableListGeneration: true
563+
artifactPath: /tmp/logs-*.tar.gz
564+
additionalArtifacts:
565+
- "/tmp/support-*.zip"
530566
- name: integration-embedded
531567
buildxOptions:
532568
enabled: true

api/resource/definitions/enums/enums.proto

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,7 @@ enum BlockVolumeType {
752752
VOLUME_TYPE_SYMLINK = 4;
753753
VOLUME_TYPE_OVERLAY = 5;
754754
VOLUME_TYPE_EXTERNAL = 6;
755+
VOLUME_TYPE_MEMORY = 7;
755756
}
756757

757758
// CriImageCacheStatus describes image cache status type.

hack/release.toml

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,52 @@ The DNS protocol can be configured on a per-name server basis in the `ResolverCo
115115
description = """\
116116
Talos now supports a new `ImageCacheConfig` document for configuring the Image Cache feature, replacing the old `machine.features.imageCache` field in the v1alpha1 config.
117117
Old configuration is still supported for backwards compatibility.
118+
"""
119+
120+
[notes.ephemeralNode]
121+
title = "Ephemeral Talos Node (tmpfs STATE and EPHEMERAL)"
122+
description = """\
123+
Both the `STATE` and `EPHEMERAL` system volumes can now be backed by `tmpfs` instead of a disk
124+
partition, turning a node into a fully ephemeral machine that loses **all** machine state,
125+
including the machine configuration itself, on every reboot.
126+
127+
Enable per volume with a `VolumeConfig` document:
128+
129+
```yaml
130+
apiVersion: v1alpha1
131+
kind: VolumeConfig
132+
name: STATE
133+
volumeType: memory
134+
---
135+
apiVersion: v1alpha1
136+
kind: VolumeConfig
137+
name: EPHEMERAL
138+
volumeType: memory
139+
provisioning:
140+
minSize: 3GiB
141+
```
142+
143+
Semantics and constraints:
144+
145+
- `STATE: memory` uses a fixed 100 MiB tmpfs; no `provisioning` or `encryption` may be configured.
146+
- `EPHEMERAL: memory` requires `provisioning.minSize` (minimum 2 GiB, taken straight out of RAM)
147+
and rejects `diskSelector`, `grow`, `maxSize`, and `encryption`.
148+
- Machine configuration must be supplied on every boot via kernel cmdline, platform metadata,
149+
or the maintenance API — nothing is persisted to disk. `talosctl apply-config --mode=no-reboot`
150+
is accepted; `--mode=reboot` and `--mode=staged` succeed but the staged config is lost when
151+
the node reboots.
152+
- The node joins the cluster with a freshly generated identity each boot; this is intended for
153+
workers and single-node clusters where ephemerality is the goal. Production multi-node control
154+
planes should keep `STATE` on disk.
155+
- Sizing: every byte of `EPHEMERAL` comes out of RAM. Plan for `minSize` + workload RAM +
156+
~512 MiB of kernel/system overhead. A 4 GiB node with a 3 GiB ephemeral volume will OOM
157+
under load; for the integration test suite the QEMU controlplane VM is provisioned with 6 GiB.
158+
- `META` is **not yet** supported on tmpfs; it stays on disk. A fully diskless node is not yet
159+
supported in this release.
160+
- The `talosctl dashboard` Talos info pane now reports the ephemeral mode of each node.
161+
162+
A dedicated CI variant `integration-qemu-ephemeral` runs a single-node cluster end-to-end with
163+
both STATE and EPHEMERAL on tmpfs.
118164
"""
119165

120166
[make_deps]

hack/test/e2e-qemu.sh

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,20 @@ case "${WITH_4K_DISK:-false}" in
239239
;;
240240
esac
241241

242+
case "${WITH_EPHEMERAL_NODE:-false}" in
243+
false)
244+
;;
245+
*)
246+
# Fully ephemeral node: STATE and EPHEMERAL on tmpfs. Forced single-node cluster.
247+
QEMU_FLAGS+=("--config-patch=@hack/test/patches/ephemeral-memory.yaml")
248+
QEMU_CONTROLPLANES=1
249+
QEMU_WORKERS=0
250+
QEMU_MEMORY_CONTROLPLANES="${QEMU_MEMORY_CONTROLPLANES:-6144}"
251+
EXTRA_TEST_ARGS="${EXTRA_TEST_ARGS:-} -talos.ephemeral-node"
252+
export EXTRA_TEST_ARGS
253+
;;
254+
esac
255+
242256
case "${WITH_UKI_BOOT:-false}" in
243257
false)
244258
;;
@@ -310,7 +324,7 @@ function create_cluster {
310324
--provisioner="${PROVISIONER}" \
311325
--name="${CLUSTER_NAME}" \
312326
--kubernetes-version="${KUBERNETES_VERSION}" \
313-
--controlplanes=3 \
327+
--controlplanes="${QEMU_CONTROLPLANES:-3}" \
314328
--workers="${QEMU_WORKERS:-2}" \
315329
--disk="${QEMU_SYSTEM_DISK_SIZE:-15360}" \
316330
--extra-disks="${QEMU_EXTRA_DISKS:-0}" \
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
---
2+
apiVersion: v1alpha1
3+
kind: VolumeConfig
4+
name: STATE
5+
volumeType: memory
6+
---
7+
apiVersion: v1alpha1
8+
kind: VolumeConfig
9+
name: EPHEMERAL
10+
volumeType: memory
11+
provisioning:
12+
minSize: 3GiB
13+
---
14+
# Single-node ephemeral cluster: schedule workloads on the control plane.
15+
cluster:
16+
allowSchedulingOnControlPlanes: true

internal/app/machined/pkg/controllers/block/internal/volumes/close.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import (
1818
// Close the encrypted volumes.
1919
func Close(ctx context.Context, logger *zap.Logger, volumeContext ManagerContext) error {
2020
switch volumeContext.Cfg.TypedSpec().Type {
21-
case block.VolumeTypeTmpfs, block.VolumeTypeDirectory, block.VolumeTypeSymlink, block.VolumeTypeOverlay, block.VolumeTypeExternal:
21+
case block.VolumeTypeTmpfs, block.VolumeTypeDirectory, block.VolumeTypeSymlink, block.VolumeTypeOverlay, block.VolumeTypeExternal, block.VolumeTypeMemory:
2222
// volume types can be always closed
2323
volumeContext.Status.Phase = block.VolumePhaseClosed
2424

0 commit comments

Comments
 (0)