Skip to content

Commit 3631ab0

Browse files
ckyrouaccgwalters
authored andcommitted
to-disk: Use symlink approach for container storage
When installing images larger than ~3GB, the installation would fail with "no space left on device" during container layer import. This occurred because the tmpfs mounts for /var/lib/containers and /var/tmp defaulted to 50% of RAM, which was insufficient for storing temporary layer data during import. Fix by mounting /var/tmp as a large tmpfs (sized to match the swap partition, which is already calculated based on image size) and symlinking /var/lib/containers to /var/tmp/containers. This consolidates temporary storage in one location and avoids duplicating size calculation logic. Fixes installation failures for images >3GB Assisted-by: Claude Code Signed-off-by: ckyrouac <[email protected]> Signed-off-by: Colin Walters <[email protected]>
1 parent e4cea7b commit 3631ab0

File tree

1 file changed

+19
-7
lines changed

1 file changed

+19
-7
lines changed

crates/kit/src/to_disk.rs

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ impl ToDiskOpts {
180180
}
181181

182182
/// Generate the complete bootc installation command arguments for SSH execution
183-
fn generate_bootc_install_command(&self) -> Result<Vec<String>> {
183+
fn generate_bootc_install_command(&self, disk_size: u64) -> Result<Vec<String>> {
184184
let source_imgref = format!("containers-storage:{}", self.source_image);
185185

186186
// Quote each bootc argument individually to prevent shell injection
@@ -206,14 +206,25 @@ impl ToDiskOpts {
206206
.map(|v| format!("--env=RUST_LOG={v}"))
207207
.unwrap_or_default();
208208

209+
// Size /var/tmp tmpfs to match swap size (disk_size)
210+
// This avoids duplicating size calculation logic
211+
let tmpfs_size_str = format!("size={}k", disk_size / 1024);
212+
let tmpfs_size_quoted = shlex::try_quote(&tmpfs_size_str)
213+
.map_err(|e| eyre!("Failed to quote tmpfs size: {}", e))?
214+
.to_string();
215+
209216
// Create the complete script by substituting variables directly
210217
let script = indoc! {r#"
211218
set -euo pipefail
212-
219+
213220
echo "Setting up temporary filesystems..."
214-
mount -t tmpfs tmpfs /var/lib/containers
215-
mount -t tmpfs tmpfs /var/tmp
216-
221+
# Mount /var/tmp as a large tmpfs, then symlink /var/lib/containers to it
222+
# to consolidate temporary storage in one location
223+
mount -t tmpfs -o {TMPFS_SIZE} tmpfs /var/tmp
224+
mkdir -p /var/tmp/containers
225+
rm /var/lib/containers -rf
226+
ln -sr /var/tmp/containers /var/lib/containers
227+
217228
echo "Starting bootc installation..."
218229
echo "Source image: {SOURCE_IMGREF}"
219230
echo "Additional args: {BOOTC_ARGS}"
@@ -237,9 +248,10 @@ impl ToDiskOpts {
237248
--skip-fetch-check \
238249
{BOOTC_ARGS} \
239250
/dev/disk/by-id/virtio-output
240-
251+
241252
echo "Installation completed successfully!"
242253
"#}
254+
.replace("{TMPFS_SIZE}", &tmpfs_size_quoted)
243255
.replace("{SOURCE_IMGREF}", &quoted_source_imgref)
244256
.replace("{INSTALL_LOG}", &install_log)
245257
.replace("{BOOTC_ARGS}", &bootc_args);
@@ -371,7 +383,7 @@ pub fn run(opts: ToDiskOpts) -> Result<()> {
371383

372384
// Phase 3: Installation command generation
373385
// Generate complete script including storage setup and bootc install
374-
let bootc_install_command = opts.generate_bootc_install_command()?;
386+
let bootc_install_command = opts.generate_bootc_install_command(disk_size)?;
375387

376388
// Phase 4: Ephemeral VM configuration
377389
let mut common_opts = opts.additional.common.clone();

0 commit comments

Comments
 (0)