zram
zram (previously called compcache) is a Linux kernel feature and set of userspace tools for creating compressible RAM-based block devices. It has been included as a module of the mainline Linux kernel since version 3.14. Starting with kernel version 3.15, zram supports multiple compression algorithms, which can be changed without a system restart.
The zram kernel module creates compressed block devices in RAM. These block devices can then be used for swap or general purpose RAM disks.
The RAM used for the block device is dynamically obtained and released up to its predefined uncompressed maximum size. The way it extends the amount of available RAM to a system is by using a portion of the RAM as compressed swap. It can therefore hold more pages of memory in the compressed swap than the amount of actual memory used.
Typically it compresses to a 3:1 ratio. So, 1 GiB of swap uses only 333 MiB of RAM on average. The compression ratio including memory used for overhead varies depending on the percent of maximum space used. This may vary from around 1.5:1 for a 1.5 GiB disk with only 5% space used, to over 3:1 when nearly full. It also is much faster at swapping pages than hard disks.
To be more memory efficient, zram offers the possibility to configure a backing store, which holds blocks that are not compressible or which have not been accessed in a while.
Combining zram with a correctly tuned Portage configuration should keep a desktop system running in a responsive manner, even during intensive software compilation.
Caveats
Systems with limited memory, non swap use can reduce the amount of available memory to run applications.
In memory limited systems, setting a large zram may cause more Out of Memory (OoM) issues. This can happen in memory intensive applications, such as compiling in Portage. zswap may be suitable for this use case, see zswap
When using this with OpenRC and an SSD, avoid setting rc_parallel=YES in /etc/rc.conf (which starts services in parallel). Depending on the size of the zram partitions and the speed of the RAM, some swap partitions and filesystems might not be ready when the swap and localmount services are started.
In such case, if parallel must be used, consider removing these services from the boot runlevel, and adding them to default instead.
Enabling zram support in kernel
Enable the following options in the kernel's configuration file:
Device Drivers --->
[*] Block devices Search for <code>CONFIG_BLK_DEV</code> to find this item. --->
<M> Compressed RAM block device support Search for <code>CONFIG_ZRAM</code> to find this item.
[*] Write back incompressible or idle page to backing device Search for <code>CONFIG_ZRAM_WRITEBACK</code> to find this item.
[*] Track zRam block status Search for <code>CONFIG_ZRAM_MEMORY_TRACKING</code> to find this item.
It is recommended that zram be built as a loadable module. This allows changing number of zram devices without rebooting, by deactivating zram devices and re-loading module with new parameters. If zram is built-in, then the number of devices can only be changed at boot time by using the kernel boot parameter zram.num_devices=#.
In order to use the LZ4 compression algorithm, enable it in kernel config:
Cryptographic API --->
Compression --->
<*> LZ4 Search for <code>CONFIG_CRYPTO_LZ4</code> to find this item.
Snippet
/etc/kernel/config.d/zram-linux6-1-111.configCONFIG_ZSMALLOC=m
CONFIG_BLK_DEV=y
CONFIG_ZRAM=m
CONFIG_ZRAM_DEF_COMP_LZ4=y
CONFIG_ZRAM_DEF_COMP="lz4"
CONFIG_ZRAM_WRITEBACK=y
CONFIG_ZRAM_MEMORY_TRACKING=y
CONFIG_CRYPTO_LZO=y
CONFIG_CRYPTO_LZ4=y
CONFIG_CRYPTO_ZSTD=y
CONFIG_ZLIB_DEFLATE=y
CONFIG_LZO_COMPRESS=y
CONFIG_LZO_DECOMPRESS=y
CONFIG_LZ4_COMPRESS=y
CONFIG_LZ4_DECOMPRESS=y
CONFIG_ZSTD_COMMON=y
CONFIG_ZSTD_COMPRESS=y
Initialization
Using zramctl
The easiest way to create a zram device is to use zramctl, which is a part of the sys-apps/util-linux package. To create a 4G zram device using zramctl with lz4 compression and use it for swap:
root #modprobe zramroot #zramctl /dev/zram0 --size 4G --algorithm lz4root #mkswap /dev/zram0root #swapon /dev/zram0See man zramctl for examples of usage.
Using OpenRC
The previous section covered initializing zram, but it is temporary and will be reset after reboot. To keep the change consistent, create two /etc/local.d files: zram.start and zram.stop. OpenRC will run these during the appropriate as part of service run process when booting or changing run levels.
For example:
- Specs: 4 cpu cores, 4G RAM total
- Configure 6G zram swap and activate.
- Estimated maximum ram used 2G @ 3:1 compression ratio.
/etc/local.d/zram.start#!/bin/bash
modprobe zram
zramctl /dev/zram0 --size 6G --algorithm lz4
mkswap /dev/zram0
swapon /dev/zram0 -p 10
/etc/local.d/zram.stop#!/bin/bash
swapoff /dev/zram0
echo 1 > /sys/block/zram0/reset
modprobe -r zram
size may also be specified using mem suffixes (K, M, G): echo 6144M > /sys/block/zram0/disksize
Now make both of these files executable:
root #chmod +x /etc/local.d/zram.start /etc/local.d/zram.stopAdd local service to default runlevel if it is not already added:
root #rc-update add local defaultUsing zram in /var/tmp/portage
While zram does improve the system responsiveness, mounting zram device in /var/tmp/portage may not show expected gain in Portage performance. Compiling is often CPU/RAM bound, and it's likely best to use zram with lz4 compression as swap.
For manual installation of zram mounted in /var/tmp/portage, the following script can be used to create 4G of zram with lzo compression:
#!/bin/bash
modprobe zram
zramctl /dev/zram0 --size 4G --algorithm lzo
mkfs.ext4 /dev/zram0
mount /dev/zram0 /var/tmp/portage
chown -R portage:portage /var/tmp/portage
Using zram-init service
The same thing can also be achieved by using Martin Väth's zram-init script which allows for easy multiple zram devices management through one script.
root #emerge --ask sys-block/zram-initOpenRC
Edit the /etc/conf.d/zram-init file and create/configure the desired zram devices. There are lots of comments/instructions in the file. So proceed with editing and be sure to save it when the appropriate modifications have been made.
Set priority of hard drive swap to low, e.g. via /etc/fstab. For multicore systems, set maxs equal to the number of cores.
An example configuration:
/etc/conf.d/zram-initload_on_start="yes"
unload_on_stop="yes"
num_devices="3"
type0="swap"
flag0=
size0="4096"
maxs0=2
algo0=lz4
type1="/tmp"
flag1="ext4"
opts1="noatime"
mode1=777
size1="1024"
labl1="zram-tmp"
type2=/var/tmp/portage
flag2=ext4
opts2="noatime"
size2=8192
algo2=zstd
owgr2="portage:portage"
mode2=775
labl2=zram-var-tmp-portage
back2=/dev/mapper/zram-nvme0n1p4
icmp2=y
idle2=600
This example creates three zram devices:
- A 4GB swap device, using the lz4 compression algorithm. Do not forget to enable the new swap device with low priority in /etc/fstab.
- A 1GB device mounted at /tmp. It is formatted with the ext4 filesystem with mount options noatime and data=writeback for optimal performance.
- An 8GB device mounted at /var/tmp/portage with a backing store located at the /dev/mapper/zram-nvme0n1p4 partition for incompressible blocks and for blocks that have not been accessed for 10 minutes. It changes the ownership to user and group portage.
Then, add the init script to the desired runlevel, usually boot, and start the service:
root #rc-update add zram-init boot
root #rc-service zram-init start
In this case the boot runlevel is preferable to the default runlevel because zram is providing temporary storage filesystems at /tmp or /var/tmp which are prerequisites for other services which will start during the default runlevel.
Be sure to monitor closely for any issues on the computer(s) for a few days when starting in the boot runlevel whilst using /tmp in zram. Additional configuration of services (utilizing rc_need or rc_want in their respective conf.d files) using a path of zram may be required.
For example, service "systemd-tmpfiles-setup" may depend on zram, if zram is providing temporary filesystem.
In this case "zram-init" should be started before "systemd-tmpfiles-setup" :
/etc/conf.d/zram-initrc_before="systemd-tmpfiles-setup"
If the config was changed, restart zram-init:
root #rc-service zram-init restart
systemd
The sys-block/zram-init package provides systemd units with self explained names:
- zram_btrfs.service
- zram_swap.service
- zram_tmp.service
- zram_var_tmp.service
These units feature default zram device settings which need to be adjusted first.
Service drop in override example
For example, to enable the /var/tmp directory (which includes Portage's temporary directory (/var/tmp/portage) used for compiling packages) in zram it is first required to override the defaults. This can be done using the systemctl edit utility:
root #systemctl edit zram_var_tmp.serviceThe empty
ExecStart= clears the list of ExecStart= commands obtained from the unit being overridden./etc/systemd/system/zram_var_tmp.service.d/override.confEdited service drop-in### Editing /etc/systemd/system/zram_var_tmp.service.d/override.conf
### Anything between here and the comment below will become the new contents of the file
[Service]
ExecStart=
ExecStart=/sbin/zram-init -d2 -s2 -alz4 -text4 -orelatime -m1777 -Lvar_tmp_dir 16384 /var/tmp
The file size should be adjusted to a size large enough to hold the working directories for the packages that will be compiled. Consider adjusting the default value from
2048 to at least 16384 (16GiB). See the dedicated article for estimates of uncompressed space necessary for successful compilation.After this configuration is finalized, the service can be enabled and activated via:
root #systemctl enable --now zram_var_tmp.serviceCreated symlink /etc/systemd/system/local-fs-pre.target.wants/zram_var_tmp.service → /etc/systemd/system/zram_var_tmp.service.
zram_var_tmp_portage.service example
It appears the .service file examples are no longer included with the zram-init package, therefore this example should aid in systems that would benefit from exclusively compressing the /var/tmp/portage directory.
/etc/systemd/system/zram_var_tmp_portage.service64 GiB of zstd compressed space example# SPDX-License-Identifier: GPL-2.0-only
[Unit]
Description=Mount /var/tmp/portage as zram
DefaultDependencies=no
Conflicts=umount.target
Before=local-fs.target umount.target
After=local-fs-pre.target
RequiresMountsFor=/var/tmp
[Service]
Type=oneshot
RemainAfterExit=true
# Adapt the maximal size (in MB) and other options like maximal number of
# parallel streams (-s) as necessary.
# Make sure to avoid collision of devices (-d...) with
# zram_swap.service and zram_tmp.service and
# that num_devices in modprobe.d/zram.conf contains the maximal used number + 1
# 64GiB of zstd compressed space
ExecStart=/sbin/zram-init -d2 -s3 -azstd -text4 -orelatime -m1777 -Lvar_tmp_portage 65536 /var/tmp/portage
ExecStop=/sbin/zram-init -d2 0 /var/tmp/portage
[Install]
WantedBy=local-fs-pre.target
After this configuration is finalized, the service can be enabled and activated via:
root #systemctl enable --now zram_var_tmp_portage.serviceCreated symlink /etc/systemd/system/local-fs-pre.target.wants/zram_var_tmp_portage.service → /etc/systemd/system/zram_var_tmp_portage.service.
Using systemd zram-generator
systemd provides an external package for configuring zram devices. To install it, run:
root #emerge --ask sys-apps/zram-generatorFor the simplest zram swap configuration, simply define an empty zram device section in the config:
root #echo '[zram0]' > /etc/systemd/zram-generator.confThis will configure a zram swap device 4GB in size or half the RAM, whichever is smaller, to start on early boot. To start it immediately, run:
root #systemctl daemon-reload
root #systemctl start dev-zram0.swap
The configuration directory can also be .d'ed:
root #mkdir /etc/systemd/zram-generator.conf.d
root #mv /etc/systemd/zram-generator.conf /etc/systemd/zram-generator.conf.d/zram0-swap.conf
For all available settings and the defaults, refer to the man page:
user $man zram-generator.conf
A more complete example for zram-swap would be:
/etc/systemd/zram-generator.conf.d/zram0-swap.conf[zram0]
zram-size = ram / 2
compression-algorithm = zstd
swap-priority = 100
fs-type = swap
Note that on systems with up to 8GB RAM, those settings are basically the same as the very simple configuration with just the line [zram0], because in the default setting half of the systems RAM is used, but not more than 4GB. Depending on the kernel configuration, the compression algorithm zstd could also be what the system defaults to. For all fs-types, including swap, options default to discard.
For more non-swap configurations, see zram-generator's README.
Using udev
Another possibility is to use existing configuration files - this option works on vanilla Gentoo without need to install additional software, also useful if using systemd instead of OpenRC. The first example can be implemented using:
/etc/udev/rules.d/10-zram.rulesKERNEL=="zram0", SUBSYSTEM=="block", DRIVER=="", ACTION=="add", ATTR{disksize}=="0", ATTR{disksize}="512M", RUN+="/sbin/mkswap $env{DEVNAME}"
KERNEL=="zram1", SUBSYSTEM=="block", DRIVER=="", ACTION=="add", ATTR{disksize}=="0", ATTR{disksize}="512M", RUN+="/sbin/mkswap $env{DEVNAME}"
KERNEL=="zram2", SUBSYSTEM=="block", DRIVER=="", ACTION=="add", ATTR{disksize}=="0", ATTR{disksize}="512M", RUN+="/sbin/mkfs.ext4 $env{DEVNAME}"
# if you want lz4 support (since kernel 3.15) and without ext4 journaling
# KERNEL=="zram2", SUBSYSTEM=="block", DRIVER=="", ACTION=="add", ATTR{initstate}=="0", ATTR{comp_algorithm}="lz4", ATTR{disksize}="512M", RUN+="/sbin/mkfs.ext4 -O ^has_journal -L $name $env{DEVNAME}"
/etc/fstab/dev/zram0 swap swap pri=16383 0 0
/dev/zram1 swap swap pri=16383 0 0
/dev/zram2 /tmp ext4 defaults 0 0
/etc/modprobe.d/zram.confoptions zram num_devices=3
systemd
If using systemd with this method, ensure that the zram module is loaded by systemd. The simplest way to achieve that is to include a file in /etc/modules-load.d/ like this one:
/etc/modules-load.d/zram.confzram
Checking that zram is used
The most compact method to check zram devices is to use zramctl
user $zramctlNAME ALGORITHM DISKSIZE DATA COMPR TOTAL STREAMS MOUNTPOINT /dev/zram0 lz4 2G 4K 64B 20K 4 [SWAP]
Troubleshooting
OpenRC: zram already mounted or mount point busy
Problem: "* Mounting local filesystems ... " doesn't wait for mkfs.ext4 to finish, thus zram will fail to mount at boot time.
Solution: Set rc_need="udev-settle" in /etc/conf.d/localmount.
This situation occurs when using mkfs.ext4 in /etc/udev/rules.d/10-zram.rules. It is caused by mkfs.ext4 being not finished running when /etc/init.d/localmount runs (even with rc_parallel="NO" in /etc/rc.conf) and thus causes mount with fail with mount: /tmp: /dev/zram1 already mounted or mount point busy.
See also
- Portage TMPDIR on tmpfs — It is unlikely that tmpfs will provide any performance gain for modern systems
- Zswap — a lightweight compressed cache for swap pages.
External resources
- zram in official kernel documentation.