{"id":13327,"date":"2026-01-20T01:01:00","date_gmt":"2026-01-20T06:01:00","guid":{"rendered":"https:\/\/www.both.org\/?p=13327"},"modified":"2026-01-14T17:35:04","modified_gmt":"2026-01-14T22:35:04","slug":"create-a-linux-kiosk-at-your-library","status":"publish","type":"post","link":"http:\/\/www.both.org\/?p=13327","title":{"rendered":"Create a Linux kiosk at your library"},"content":{"rendered":"<div class=\"pld-like-dislike-wrap pld-template-1\">\r\n    <div class=\"pld-like-wrap  pld-common-wrap\">\r\n    <a href=\"javascript:void(0)\" class=\"pld-like-trigger pld-like-dislike-trigger  \" title=\"\" data-post-id=\"13327\" data-trigger-type=\"like\" data-restriction=\"cookie\" data-already-liked=\"0\">\r\n                        <i class=\"fas fa-thumbs-up\"><\/i>\r\n                <\/a>\r\n    <span class=\"pld-like-count-wrap pld-count-wrap\">2    <\/span>\r\n<\/div><\/div>\n<p>As a frequent client of my local library, I&#8217;m very invested in the health, sustainability, and privacy of library systems all over the world. When my friend and fellow Both.org author, Don Watkins, mentioned a project he was working on to deliver his library with computers so people could do research on them, he also mentioned that he needed the computers to erase all user data after the user has logged out of their session. I&#8217;m interested in &#8220;kiosk mode&#8221; for Linux computers, and I&#8217;ve used distributions like <a href=\"https:\/\/www.porteus.org\/\">Porteus<\/a> as a Kiosk at computer conferences, but recently I&#8217;ve just been using GNOME Display Manager (GDM) on any distribution that makes GDM available. Here&#8217;s how to use Linux and GDM to create a Guest mode for your Linux computer.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Start without a guest account<\/h2>\n\n\n\n<p>The first few steps of this process don&#8217;t actually require a guest user directory to exist, so <em>do NOT create your guest user account yet<\/em>. However, you do need to choose what your guest user account is going to be called. A reasonable account name for Don&#8217;s purposes is <code>libraryguest<\/code>. On my personal computer I call my guest account <code>guestaccount<\/code>, and I&#8217;ve used <code>kioskguest<\/code> on some installations. I avoid just the name &#8220;guest&#8221; because in modern computing the term &#8220;guest&#8221; gets used in a few other ways (such as a &#8220;guest operating system&#8221; in a virtual environment), and it&#8217;s just easier to find something unique in logs.<\/p>\n\n\n\n<p>Choose a unique name for you guest account, but don&#8217;t create it yet. For this article, I&#8217;m using <code>libraryguest<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Create the PostSession script<\/h2>\n\n\n\n<p>By default, GDM recognises several states: Init, PostLogin, PreSession, and PostSession. Each state has a directory located in <code>\/etc\/gdm<\/code>. When you place a shell script called <code>Default<\/code> in one of those directories, GDM runs the script when it reaches that state.<\/p>\n\n\n\n<p>To trigger actions to clean up a user&#8217;s environment upon logout, create the file <code>\/etc\/gdm\/PostSession\/Default<\/code>. You can add whatever actions you want to run upon logout to the <code>Default<\/code> script. In the case of Don&#8217;s library, we wanted to clear everything from the guest&#8217;s home directory, including browser history, any LibreOffice files or GIMP files they may have created, and so on. It was important that we limited the very drastic action of removing all user data to <em>just<\/em> the guest user. We didn&#8217;t want the admin&#8217;s data to be erased upon logout, so whatever rule we added to <code>\/etc\/gdm\/PostSession\/Default<\/code> had to be limited to the guest user.<\/p>\n\n\n\n<p>Here&#8217;s what we came up with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/usr\/bin\/sh\n\necho \"$USER logged out at `date`\" &gt;&gt; \/tmp\/PostSession.log\n\nif &#91; \"X$USER\" = \"Xlibraryguest\" ]; then\n    rm -rf \"$HOME\"\nfi\nexit 0<\/code><\/pre>\n\n\n\n<p>The first line is for logging purposes. The <code>\/tmp<\/code> directory gets cleared out on most distributions automatically, so we weren&#8217;t worried about creating a file that&#8217;ll grow forever and eventually crash the computer. If your distribution of choice doesn&#8217;t clean out <code>\/tmp<\/code> automatically, <a href=\"https:\/\/www.both.org\/?p=3685\">create a cron job<\/a> to do that for you.<\/p>\n\n\n\n<p>GDM knows what user triggered the logout process, so the <code>if<\/code> statement verifies that the user logging out is definitely the <code>libraryguest<\/code> user (that&#8217;s the literal name of the user we created for library patrons).<br>Note that the whitespace around the square brackets is important, so be precise when typing!<\/p>\n\n\n\n<p>As long as it is <code>libraryguest<\/code>, then the script removes the <em>entire user directory<\/em> (<code>$HOME<\/code>). That can be extremely dangerous if you make a mistake, so do thorough testing on a dummy system before implementing a script like this! If you get a condition wrong, you could erase <em>your entire home directory<\/em> upon logout.<\/p>\n\n\n\n<p>In this example, I&#8217;ve successfully limited the <code>rm<\/code> command to a logout action performed by user <code>libraryguest<\/code>. The entire <code>\/home\/libraryguest<\/code> directory is erased, and the computer returns to the GDM login screen. When a new user logs in, a fresh directory is created for the user.<\/p>\n\n\n\n<p>You can put any number of commands in your script, of course. You don&#8217;t have to erase an entire directory. If all you really want to do is clear browser history and any stray data, then you can do that instead. If you need to copy specific configuration files into the environment, you can do that during the PreSession state.<\/p>\n\n\n\n<p>Just be sure to test thoroughly before committing your creation to your users!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">What happens when the guest doesn&#8217;t log out<\/h2>\n\n\n\n<p>At this point, the computer erases all of the user&#8217;s data when the user logs out, but a reboot or a shutdown is different to a logout. GDM doesn&#8217;t enter a PostSession state after a reboot signal has been received, even if the reboot occurs during an active GDM session.<\/p>\n\n\n\n<p>The easiest and safest way to erase an entire home directory when there&#8217;s a cut to system power is to use a temporary RAM filesystem (<code>tmpfs<\/code>) to house the data in the first place. If the systems you&#8217;re configuring have 8 GB or more, and the system is exclusively used as a guest computer, you can probably afford to use RAM as the guest&#8217;s home directory. If your system doesn&#8217;t have a lot of RAM, then you can use the systemd work-around in the next section.<\/p>\n\n\n\n<p>Assuming you have the RAM to spare, and that your systems are supported by a backup power supply, you can add a <code>tmpfs<\/code> entry in <code>\/etc\/fstab<\/code>. In this example, my <code>tmpfs<\/code> is mounted to <code>\/home\/libraryguest<\/code> and is just 2 GB:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>tmpfs     \/home\/libraryguest    tmpfs    rw,nosuid,nodev,size=2G   0 0<\/code><\/pre>\n\n\n\n<p>That&#8217;s plenty of space for some Internet browsing and even a few LibreOffice documents to be saved while a user works.<\/p>\n\n\n\n<p>Mount the new volume:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ sudo mount \/home\/libraryguest<\/code><\/pre>\n\n\n\n<p>Next, you must create the <code>libraryguest<\/code> user manually in a terminal.<br>The <code>useradd<\/code> command creates user profiles:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ sudo useradd --home-dir \/home\/libraryguest libraryguest\nuseradd: warning: the home directory \/home\/libraryguest\/ already exists.\nuseradd: Not copying any file from skel directory into it.<\/code><\/pre>\n\n\n\n<p>Because you&#8217;ve already created a location for the home directory, you do get a warning after creating the user. It&#8217;s only a warning, not a fatal error, and the guest account is automatically populated later.<\/p>\n\n\n\n<p>Create a password for the new user:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ sudo passwd libraryguest<\/code><\/pre>\n\n\n\n<p>That&#8217;s it! You&#8217;ve created a guest account that refreshes with every logout and every reboot. You can skip over the next section of this article.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Using systemd targets instead of a ramdisk<\/h2>\n\n\n\n<p>Assuming you can&#8217;t create a ramdisk for temporary user data, you can instead create a systemd service that runs a script when the reboot, poweroff, and multi-user targets are triggered:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&#91;Unit]\nDescription=Kiosk cleanup\n\n&#91;Service]\nType=oneshot\nExecStart=\/usr\/local\/bin\/kiosk-cleanup.sh\n\n&#91;Install]\nWantedBy=poweroff.target reboot.target multi-user.target<\/code><\/pre>\n\n\n\n<p>Save the file to <code>\/etc\/systemd\/system\/kioskmode.service<\/code> and then enable it:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ sudo systemctl enable --now kioskmode<\/code><\/pre>\n\n\n\n<p>The script, like the GDM script, removes the <code>libraryguest<\/code> directory. Unlike GDM script, this one must also recreate an empty home directory and grant it user permissions:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/usr\/bin\/bash\n\nrm -rf \/home\/libraryguest\nmkdir \/home\/libraryguest\nchown -R libraryguest:libraryguest \/home\/libraryguest<\/code><\/pre>\n\n\n\n<p>Grant the script itself permission to run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ sudo chmod +x \/usr\/local\/bin\/kiosk-cleanup.sh<\/code><\/pre>\n\n\n\n<p>Now the <code>libraryguest<\/code> user data is erased after:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Logout<\/li>\n\n\n\n<li>Reboot<\/li>\n\n\n\n<li>Shutdown<\/li>\n\n\n\n<li>Startup<\/li>\n<\/ul>\n\n\n\n<p>Essentially, no matter how the computer loses its session or its power, the <code>libraryguest<\/code> account starts fresh when a new session is started.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Security and privacy<\/h3>\n\n\n\n<p>Using systemd to erase data at shutdown and startup isn&#8217;t strictly as secure as using a temporary ramdisk for all user data. Should the computer lose power suddenly, all saved user data in the <code>libraryguest<\/code> account is present during the next boot. Of course, it&#8217;s erased as soon as <code>multi-user.target<\/code> is called by systemd, but it is technically possible to interrupt the boot process and mine for data. You must use full drive encryption to protect data from being discovered by an interrupted boot sequence.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why not just use xguest<\/h2>\n\n\n\n<p>On many Linux distributions, the <code>xguest<\/code> package is designed to provide the <strong>Guest<\/strong> account, which resets after each logout. It was an extremely useful package that I installed on every machine I owned, because it&#8217;s handy to be able to let friends use my computer without risking them making a mess of my home directory. Lately, it seems that <code>xguest<\/code> is failing to launch a desktop, however, presumably because it relies on X11.<\/p>\n\n\n\n<p>If <code>xguest<\/code> works for you in your tests, then you may want to use it instead of the solution I&#8217;ve presented here. My solution offers a lot of flexibility, thanks to GDM&#8217;s autodetection of session states.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Kiosks in libraries<\/h2>\n\n\n\n<p>Privacy and personal information is more important than ever. Regardless of how you setup a kiosk for your library, you have an obligation to your users to keep them informed of how their data is being stored. This goes both ways. Users need to know that their data is destined to be erased as soon as they log out, and also they deserve to be assured that their data is not retained.<\/p>\n\n\n\n<p>However, it&#8217;s also your responsibility to admit that glitches and exceptions could occur. Users need to understand that the computer they&#8217;re using are public computers on a public network. Encryption is being used for traffic and for data storage, but you cannot guarantee absolute privacy.<\/p>\n\n\n\n<p>As long as everyone understands the arrangement, everyone can compute with confidence. Linux, GDM, and systemd are great tools to help libraries create a sustainable, robust, honest, and communal computing platform.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>2 As a frequent client of my local library, I&rsquo;m very invested in the health, sustainability, and privacy<\/p>\n","protected":false},"author":31,"featured_media":13343,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_lmt_disableupdate":"","_lmt_disable":"","footnotes":""},"categories":[482],"tags":[],"class_list":["post-13327","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-advanced"],"modified_by":"Seth Kenlon","_links":{"self":[{"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/13327","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/users\/31"}],"replies":[{"embeddable":true,"href":"http:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=13327"}],"version-history":[{"count":4,"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/13327\/revisions"}],"predecessor-version":[{"id":13342,"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/13327\/revisions\/13342"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/media\/13343"}],"wp:attachment":[{"href":"http:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=13327"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=13327"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=13327"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}