- Bash
- ramdisk.sh
Creates and mounts a proper RAMdisk of custom size. (tmpfs IS NOT A REAL RAMDISK AND CAN SWAP!)
- ramdisk.sh
wget -q https://kps.makz.net/run/ramdisk.sh
#!/bin/bash
#
# v3.0 — Create, inspect, or destroy a RAM disk at /srv/ramdisk.
#
# Backend is chosen automatically:
# - kernel >= 6.4 : tmpfs with noswap (lazy alloc, no module, no format)
# - kernel < 6.4 : brd block device + ext4 (real pinned RAM)
#
set -euo pipefail
readonly MOUNT="/srv/ramdisk"
readonly DEV="/dev/ram0"
readonly LABEL="RAMDISK"
readonly STATE_FILE="/run/ramdisk.backend" # remembers which backend was used
readonly DEFAULT_SIZE_GB=1
# ---------- output helpers ----------
if [[ -t 1 ]]; then
readonly BOLD=$(tput bold) NORMAL=$(tput sgr0)
readonly RED=$(tput setaf 1) GREEN=$(tput setaf 2)
else
readonly BOLD="" NORMAL="" RED="" GREEN=""
fi
die() { echo "${RED}Error:${NORMAL} $*" >&2; exit 1; }
info() { echo "$*"; }
usage() {
cat <<EOF
Usage: $0 [-s SIZE_GB] [-i] [-r] [-f] [-b BACKEND] [-h]
Without arguments, creates a ${DEFAULT_SIZE_GB}G RAM disk at ${MOUNT}.
-s N Create a RAM disk of N gigabytes at ${MOUNT}.
-i Show RAM disk status.
-r Unmount and remove the RAM disk.
-f With -r: forcefully kill processes using ${MOUNT} first.
-b BACKEND Force a backend: 'tmpfs' or 'brd'. Default: auto by kernel version.
-h Show this help.
EOF
}
# ---------- backend selection ----------
# Returns "tmpfs" if running kernel >= 6.4, else "brd".
detect_backend() {
local kver major minor
kver=$(uname -r)
major=${kver%%.*}
minor=${kver#*.}; minor=${minor%%.*}
# Numeric guard in case uname -r returns something weird.
[[ "$major" =~ ^[0-9]+$ && "$minor" =~ ^[0-9]+$ ]] || { echo "brd"; return; }
if (( major > 6 )) || (( major == 6 && minor >= 4 )); then
echo "tmpfs"
else
echo "brd"
fi
}
# ---------- args ----------
size_gb=$DEFAULT_SIZE_GB
action="create"
force=0
backend=""
while getopts "fhirs:b:" opt; do
case "$opt" in
f) force=1 ;;
h) usage; exit 0 ;;
i) action="info" ;;
r) action="remove" ;;
s) size_gb="$OPTARG" ;;
b) backend="$OPTARG" ;;
*) usage >&2; exit 2 ;;
esac
done
[[ "$size_gb" =~ ^[0-9]+$ ]] && (( size_gb > 0 )) \
|| die "Size must be a positive integer (GB). Got: $size_gb"
if [[ -n "$backend" ]]; then
[[ "$backend" == "tmpfs" || "$backend" == "brd" ]] \
|| die "Backend must be 'tmpfs' or 'brd'. Got: $backend"
fi
# ---------- backend implementations ----------
create_tmpfs() {
info "Backend: ${BOLD}tmpfs${NORMAL} (kernel $(uname -r), noswap supported)"
mkdir -p "$MOUNT"
mount -t tmpfs -o "size=${size_gb}G,mode=1777,noswap" tmpfs "$MOUNT"
# Confirm noswap actually took effect (older /proc/mounts won't list it
# if mount silently dropped it on an unsupported kernel).
if ! grep -qE "[[:space:]]${MOUNT}[[:space:]].*\bnoswap\b" /proc/mounts; then
umount "$MOUNT"
die "tmpfs noswap option not honored by kernel. Use '-b brd' instead."
fi
}
create_brd() {
info "Backend: ${BOLD}brd${NORMAL} (kernel $(uname -r), tmpfs noswap unavailable)"
local size_kib=$(( size_gb * 1024 * 1024 ))
if [[ -d /sys/module/brd ]]; then
local existing_kib
existing_kib=$(blockdev --getsize64 "$DEV" 2>/dev/null | awk '{print int($1/1024)}')
if [[ -z "$existing_kib" ]] || (( existing_kib != size_kib )); then
die "brd already loaded with a different size (${existing_kib} KiB vs ${size_kib} KiB requested). Unload it first: 'modprobe -r brd' (after unmounting users)."
fi
info "brd already loaded at requested size."
else
info "Loading brd module (rd_size=${size_kib} KiB)..."
modprobe brd rd_nr=1 max_part=1 "rd_size=${size_kib}"
fi
info "Formatting ${DEV} as ext4..."
mkfs.ext4 -q -m 0 -L "$LABEL" -E lazy_itable_init=0,lazy_journal_init=0 "$DEV"
mkdir -p "$MOUNT"
mount -o noatime "$DEV" "$MOUNT"
chmod 1777 "$MOUNT"
}
remove_tmpfs() {
umount "$MOUNT"
rmdir "$MOUNT" 2>/dev/null || true
}
remove_brd() {
umount "$MOUNT"
blockdev --flushbufs "$DEV" 2>/dev/null || true
rmdir "$MOUNT" 2>/dev/null || true
if [[ -r /sys/module/brd/refcnt ]] && [[ $(< /sys/module/brd/refcnt) -eq 0 ]]; then
modprobe -r brd 2>/dev/null || true
fi
}
# ---------- actions ----------
case "$action" in
info)
if mountpoint -q "$MOUNT"; then
if [[ -f "$STATE_FILE" ]]; then
info "Backend: ${BOLD}$(<"$STATE_FILE")${NORMAL}"
fi
df -h "$MOUNT"
exit 0
else
info "No RAM disk at ${MOUNT}"
exit 1
fi
;;
remove)
[[ $EUID -eq 0 ]] || die "Must be run as root."
mountpoint -q "$MOUNT" || die "No RAM disk mounted at ${MOUNT}"
if (( force )); then
info "Killing processes using ${MOUNT}..."
fuser -km "$MOUNT" 2>/dev/null || true
sleep 1
fi
# Trust the state file over the CLI flag — we need to clean up the actual backend.
used_backend=$(<"$STATE_FILE" 2>/dev/null || echo "")
if [[ -z "$used_backend" ]]; then
# No state file (older mount, or someone deleted /run state).
# Guess from /proc/mounts.
if grep -qE "[[:space:]]${MOUNT}[[:space:]]tmpfs[[:space:]]" /proc/mounts; then
used_backend="tmpfs"
else
used_backend="brd"
fi
fi
info "Removing RAM disk (backend: ${used_backend})..."
if [[ "$used_backend" == "tmpfs" ]]; then
remove_tmpfs
else
remove_brd
fi
rm -f "$STATE_FILE"
info "${GREEN}RAM disk removed.${NORMAL}"
exit 0
;;
create)
[[ $EUID -eq 0 ]] || die "Must be run as root."
if mountpoint -q "$MOUNT"; then
info "RAM disk already mounted at ${MOUNT}. Exiting."
df -h "$MOUNT"
exit 0
fi
avail_kib=$(awk '/^MemAvailable:/ {print $2}' /proc/meminfo)
need_kib=$(( size_gb * 1024 * 1024 ))
(( avail_kib >= need_kib )) \
|| die "Not enough available RAM: need ${need_kib} KiB, have ${avail_kib} KiB."
[[ -z "$backend" ]] && backend=$(detect_backend)
if [[ "$backend" == "tmpfs" ]]; then
create_tmpfs
else
create_brd
fi
# Remember which backend was used so -r can clean up correctly.
echo "$backend" > "$STATE_FILE"
info "${GREEN}Done.${NORMAL} ${size_gb}G RAM disk mounted at ${BOLD}${MOUNT}${NORMAL}"
df -h "$MOUNT"
;;
esac