Limit chromium kiosk memory usage with systemd-run

5 min read · Published November 9, 2026

When a kiosk browser leaks, cap it with systemd-run so the leak ends in a browser restart and the board stays up. On a 2 GB board, start at MemoryMax=500M with CPUQuota=75%, add a relaunch loop, and confirm with free -h over the following days.

Cap the browser's memory with systemd-run and the freeze stops being a board problem. On a 2 GB kiosk board, launch the browser like this: systemd-run --scope -p MemoryMax=500M -p CPUQuota=75% followed by exactly the browser launch line you already use, wrapped in a loop that relaunches it on exit. A browser that leaks now hits the cap, the kernel kills it inside its own group, and the loop starts a fresh copy while the board keeps the rest of its RAM. This page lists the symptoms, two checks to run first, the launch-line change, the arithmetic that says whether 500 MB is right for your board, and how to confirm the fix holds.

Symptoms and what each one points to

  • Freezes after days, SSH still answers. free -h shows memory nearly gone, and killing the browser restores the picture. That is a leak in the browser or its page, and it is exactly the case a cap fixes.
  • The whole board goes dark, SSH included. The browser ran uncapped, so the kernel had nothing left for the display stack or a login shell. Set the cap first.
  • Limits are set but memory climbs past them. The memory controller is switched off at boot: look for cgroup_disable=memory on the kernel's boot command line, printed in /proc/cmdline. While that switch is present, no memory cap can apply.
  • The browser turns sluggish the moment you cap it. The cap or the CPU quota sits too close to normal use. Work the headroom rule in the example below.
  • free -h climbs for days but nothing freezes. Check whether compressed-RAM swap (zram) is enabled. It stretches a small board and hides the growth trend, so read the swap line too.

Two checks before you set the cap

Measure the healthy footprint. Restart the board clean, start the browser as usual, and run free -h. Write down the used figure; every decision below hangs on that number.

Confirm the memory controller is on. If cgroup_disable=memory appears on the boot command line, remove it and reboot. With the controller disabled, systemd cannot enforce any memory limit, so every value you set is decoration.

The fix: cap the browser at launch

  1. Open whatever launches the browser: the desktop entry or autostart script your kiosk setup uses. It is the same launch line described step by step in the chromium kiosk autostart walkthrough.
  2. Put the systemd-run --scope prefix from the introduction in front of the browser command. --scope runs the browser right there in place, inside a transient group, so the prefix works in a desktop entry with no service file.
  3. If your systemd build rejects MemoryMax=, use MemoryLimit= with the same size. That is the older property name, and it is the form shown in desktop entries that already work on cgroup v1 setups.
  4. Optionally add -p MemoryHigh= one step below the cap. The kernel then starts reclaiming memory before the hard ceiling, turning a sudden kill into a gradual squeeze. systemd's own documentation recommends MemoryHigh as the main control, with MemoryMax as the hard limit.
  5. Wrap the whole line in a shell loop that relaunches it whenever the browser exits. When the cap kills the browser, the loop starts a fresh copy and the screen comes back on its own.
  6. If the launch line stops firing after the edit, that is a different fault. The article on why a wayfire autostart line never launches the kiosk browser covers exactly that case.

Worked example: sizing the cap for a 2 GB kiosk board

Inputs. Total RAM 2048 MB, the 2 GB board this page is written around. Cap candidate 500 MB, the value shown in a working kiosk desktop entry. CPU quota 75% of one core, with 50% as the tighter option. Four cores on the board. And a free -h reading at the old freeze showing essentially all 2048 MB in use.

Rule in words. Size the cap at roughly twice the browser's measured fresh-start footprint. The factor of two is spike room: browser usage jumps when a heavy page loads or video decodes, and a cap close to the healthy reading kills the browser during normal work, not at the leak.

Arithmetic. The 500 MB cap leaves 2048 MB - 500 MB = 1548 MB for the kernel, the display stack and the file cache. As a share of RAM, 500 / 2048 = 0.244, so the browser is confined to about 24% of physical memory. CPUQuota=75% holds the browser to 0.75 of one core; across the board's four cores that is 75 / 4 = 18.75% of total CPU time. The headroom test: 500 MB / 2 = 250 MB, so the fresh-start reading must be 250 MB or less for this cap to hold.

Result. With a fresh start at or under 250 MB, the browser has 250 MB of growth room. When the leak consumes it, the kernel kills the largest process in the browser's group and the loop relaunches it. The board keeps its 1548 MB throughout, so SSH, the display stack and the relaunch loop never run short.

Decision. Start at MemoryMax=500M on this board. Raise to MemoryMax=1G only when a fresh start reads above 250 MB; the 1 GB cap leaves 2048 - 1024 = 1024 MB for everything else. If the freeze repeats with the cap in place, check for cgroup_disable=memory before touching the numbers.

How to check it worked

  • Right after a clean restart, free -h reads back near the fresh-start figure with the browser running under its cap.
  • Over the following days, used memory climbs toward the cap and then drops sharply. The screen blinks and returns. That blink is the cap doing its job: the browser was killed and relaunched.
  • Through all of it, SSH keeps answering. The failure that used to take the board down now costs one browser restart.
  • If the browser dies within minutes of starting instead, the cap is under the healthy footprint. Measure again and double it.

Recommendation: On a 2 GB kiosk board, cap the browser at 500 MB with CPUQuota=75% and a relaunch loop, and treat a fresh-start reading above 250 MB as the signal to raise the cap to 1 GB. Drop the quota to 50% only when other work on the board must be protected; 75% keeps the kiosk responsive. Leave the CPU quota out entirely when memory is the only problem.

When the cap is not the whole answer

A cap contains a leak; it does not remove it. Three ways to shrink the leak itself:

Update or lighten. Some browser leaks are fixed in newer builds of the software, as in one documented browser memory leak resolved by a software update. A lighter page does the same job with less at stake.

Take the 32-bit build. On a 2 GB board, a 32-bit browser build gives every process a far smaller address-space ceiling, so the same leak hits a wall much earlier. One 2 GB kiosk ran 30 days without a repeat after exactly that switch.

Skip the browser entirely. If the screen's whole job is looping images and video, our shop-floor signage displays play from microSD or USB with no subscription, so there is no browser to leak.

Questions

What is the difference between MemoryMax= and MemoryLimit=?

MemoryLimit= belongs to cgroup v1 setups; MemoryMax= is the current hard limit on cgroup v2, which is what recent boards run. Both accept the same size syntax, so 500M is the same ceiling either way, and if your build rejects one name you use the other. Whichever you pick, systemd's documentation recommends a soft MemoryHigh= alongside it, so the kernel reclaims memory before the hard ceiling kills anything.

Does the memory cap fix the leak itself?

No. The cap contains the damage: when the browser's group reaches the ceiling, the kernel kills the largest process inside that group and the relaunch loop brings the browser back, so the board never goes down. You can tell a cap kill from a random crash in the kernel log, because the entry names the process inside the browser's group. The leak itself only ends with a browser update, a lighter page, or a 32-bit build on a small board.

Why is the kiosk slow after I set the cap?

Check two knobs. A CPUQuota of 50% holds the browser to half of one core, and heavy pages render visibly slower at that rate; 75% gives most of the responsiveness back while still bounding CPU. The other cause is a cap set close to the fresh-start footprint, which makes the browser thrash before it dies. Keep the cap near twice the measured fresh-start reading and raise the quota first.

Planning information, not legal or engineering advice. Rules differ by place: check codes and permits with your local authority and your licensed trades.

Keep reading

Build your signAsk for a quote