<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Permissions – Serverküche</title><link>https://serverkueche.de/en/tags/permissions/</link><description>Permissions – Neueste Beiträge von Serverküche</description><generator>Hugo</generator><language>en-US</language><managingEditor>feedback@serverkueche.de (Serverküche)</managingEditor><webMaster>feedback@serverkueche.de (Serverküche)</webMaster><copyright>2026 Serverküche</copyright><lastBuildDate>Sat, 22 Aug 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://serverkueche.de/en/tags/permissions/index.xml" rel="self" type="application/rss+xml"/><item><title>Understanding Linux Users, Groups &amp; File Permissions</title><link>https://serverkueche.de/en/tutorials/linux-users-and-permissions/</link><pubDate>Sat, 22 Aug 2026 00:00:00 +0000</pubDate><author>feedback@serverkueche.de (Serverküche)</author><guid>https://serverkueche.de/en/tutorials/linux-users-and-permissions/</guid><description>Who is allowed to do what on your server? This foundational guide explains users, groups and file permissions with real commands – the basis of every setup.</description><content:encoded><![CDATA[<p>Almost every &ldquo;Permission denied&rdquo; message, every container that won&rsquo;t start and every insecure config has the same root cause: a misunderstanding of who is allowed to do what on a Linux server. This guide clears that up for good – with real commands instead of theory.</p>
<h2 id="what-are-we-building">What are we building?</h2>
<p>Not a service this time, but the foundation every other recipe stands on: the Linux permission model. By the end you&rsquo;ll understand what a user and a group are, how to read and set <code>rwx</code> permissions and the numbers like <code>755</code>, why <code>www-data</code> and <code>docker</code> keep showing up as groups – and when <code>sudo</code> is the right tool and when it isn&rsquo;t. All examples were run on <strong>Debian 13</strong> and work on any Linux server.</p>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>A Linux server with root or <code>sudo</code> access (your <a href="/en/tutorials/first-steps-netcup-vps/">netcup VPS</a> will do)</li>
<li>Familiarity with the <a href="/en/tutorials/essential-terminal-commands/">essential terminal commands</a> (<code>ls</code>, <code>cd</code>, <code>cat</code>)</li>
<li>No extra package needed – all tools ship with Debian</li>
</ul>
<div class="not-prose my-6 overflow-hidden rounded-xl border border-paprika-200 bg-paprika-50 dark:border-paprika-800 dark:bg-paprika-900/20"
     data-track-content data-content-name="Affiliate-Box · /en/tutorials/linux-users-and-permissions/" data-content-piece="VPS 1000 G12">
  <div class="flex items-center justify-between border-b border-paprika-200 bg-paprika-100 px-4 py-1.5 text-xs font-semibold uppercase tracking-wide text-paprika-700 dark:border-paprika-800 dark:bg-paprika-900/40 dark:text-paprika-300">
    <span>🍳 Recommendation</span>
    <span title="Links marked with * are affiliate links.">Ad</span>
  </div>
  <div class="flex flex-col gap-4 p-4 sm:flex-row sm:items-center sm:justify-between">
    <div>
      <p class="text-lg font-bold text-slate-900 dark:text-white">VPS 1000 G12</p>
      <p class="mt-1 text-sm text-slate-600 dark:text-slate-300">4 vCores · 8 GB RAM · 256 GB NVMe</p>
      <p class="mt-1 text-sm font-semibold text-paprika-700 dark:text-paprika-400">from €10.36/month</p>
      <p class="mt-2 text-sm text-slate-600 dark:text-slate-400">Any server works for practice – the commands are the same on every Linux.</p>
    </div>
    <a href="https://www.netcup.com/en/server/vps/vps-1000-g12-iv-12m?ref=44083" rel="sponsored noopener" target="_blank"
   data-track-event="Affiliate|netcup: Affiliate-Box|VPS 1000 G12 · {page}"
   class="inline-flex shrink-0 items-center justify-center rounded-lg bg-paprika-600 px-5 py-2.5 font-semibold text-white transition-colors hover:bg-paprika-700">
  Go to netcup →
</a>

  </div><div class="px-4 pb-4"><p class="not-prose my-3 flex flex-wrap items-center gap-x-2 gap-y-1 rounded-lg border border-herb-500/40 bg-herb-50 px-3 py-2 text-sm text-slate-700 dark:bg-herb-900/20 dark:text-slate-200">
  <span>💶 <strong>5 € voucher</strong> for new netcup customers:</span><code data-track-voucher="36nc17844976032"
        class="rounded bg-white px-2 py-0.5 font-mono text-sm font-semibold text-herb-800 dark:bg-slate-800 dark:text-herb-400">36nc17844976032</code>
  <span class="text-xs text-slate-500 dark:text-slate-400">(new customers only, no domains)</span>
</p></div>
</div>

<h2 id="step-by-step">Step by step</h2>
<h3 id="step-1-who-am-i-users-and-their-ids">Step 1: Who am I? Users and their IDs</h3>
<p>Every process and every file belongs to a <strong>user</strong>. Who you currently are is shown by <code>id</code>:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">id</span></span></code></pre></div>
</div>
<div class="sk-code">
  <span class="sk-code-head">Ausgabe</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">uid=0(root) gid=0(root) groups=0(root)</span></span></code></pre></div>
</div>
<p>Three things here: the <strong>UID</strong> (user ID, <code>0</code> = root, the administrator), the primary <strong>GID</strong> (group ID) and every group you&rsquo;re a member of. On Debian, human users get UIDs from <code>1000</code> up; below that are system users for services.</p>
<p>Users live in <code>/etc/passwd</code> – a plain text file:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">grep -E <span class="s1">&#39;^(root|www-data|nobody):&#39;</span> /etc/passwd</span></span></code></pre></div>
</div>
<div class="sk-code">
  <span class="sk-code-head">Ausgabe</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">root:x:0:0:root:/root:/bin/bash
</span></span><span class="line"><span class="cl">www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
</span></span><span class="line"><span class="cl">nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin</span></span></code></pre></div>
</div>
<p>The fields are name, password placeholder (<code>x</code> = the real hash lives in <code>/etc/shadow</code>), UID, GID, comment, home directory and login shell. Note that <code>www-data</code> – the user web servers run as – has the shell <code>/usr/sbin/nologin</code>. Such <strong>service users</strong> deliberately can&rsquo;t log in; they exist only so a process runs with minimal privileges instead of as root.</p>
<h3 id="step-2-creating-a-user">Step 2: Creating a user</h3>
<p>For everything except the initial setup you shouldn&rsquo;t work as root. Create a normal user – here <code>cook</code>:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">useradd -m -s /bin/bash cook</span></span></code></pre></div>
</div>
<p><code>-m</code> creates the home directory <code>/home/cook</code>, <code>-s /bin/bash</code> sets the login shell. Set a password with <code>passwd cook</code>. Check the result:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">id cook</span></span></code></pre></div>
</div>
<div class="sk-code">
  <span class="sk-code-head">Ausgabe</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">uid=1000(cook) gid=1000(cook) groups=1000(cook)</span></span></code></pre></div>
</div>
<p><code>useradd</code> automatically created a <strong>primary group</strong> of the same name, <code>cook</code> (GID 1000). That&rsquo;s the Debian default: every user gets their own group – so a new file is never accidentally readable by others just because they share a common group.</p>
<h3 id="step-3-reading-permissions--the-rwx-model">Step 3: Reading permissions – the <code>rwx</code> model</h3>
<p>Every file carries three sets of permissions: for the <strong>owner</strong>, for the <strong>group</strong> and for <strong>everyone else</strong>. Look at them with <code>ls -l</code>:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">ls -l</span></span></code></pre></div>
</div>
<div class="sk-code">
  <span class="sk-code-head">Ausgabe</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">-rw-r--r-- 1 root root  0 Jul 24 19:02 file.txt
</span></span><span class="line"><span class="cl">drwxr-xr-x 2 root root 40 Jul 24 19:02 folder</span></span></code></pre></div>
</div>
<p>The first column is the key. Break down <code>drwxr-xr-x</code>:</p>
<ul>
<li><strong>Character 1</strong> – the type: <code>-</code> file, <code>d</code> directory, <code>l</code> symbolic link.</li>
<li><strong>Characters 2–4</strong> (<code>rwx</code>) – the <strong>owner&rsquo;s</strong> permissions: read (<code>r</code>), write (<code>w</code>), execute (<code>x</code>).</li>
<li><strong>Characters 5–7</strong> (<code>r-x</code>) – the <strong>group&rsquo;s</strong> permissions.</li>
<li><strong>Characters 8–10</strong> (<code>r-x</code>) – <strong>everyone else&rsquo;s</strong> permissions.</li>
</ul>
<p>Then come the owner (<code>root</code>) and group (<code>root</code>). So for <code>file.txt</code> only root may write, everyone may read.</p>
<div class="not-prose my-6 rounded-lg border-l-4 p-4 border-sky-300 bg-sky-50 dark:border-sky-800 dark:bg-sky-900/20">
  <p class="mb-1 flex items-center gap-2 font-semibold text-slate-900 dark:text-white">
    <span aria-hidden="true">ℹ️</span>x means something different on directories
  </p>
  <div class="prose-kitchen text-sm">On a file, <code>x</code> means &ldquo;executable&rdquo; (a program or script). On a <strong>directory</strong>, <code>x</code> means &ldquo;allowed to enter&rdquo; (<code>cd</code>). A folder without <code>x</code> can&rsquo;t be entered even if you have <code>r</code> – a common trap.</div>
</div>
<h3 id="step-4-permissions-as-a-number--why-755-and-644">Step 4: Permissions as a number – why <code>755</code> and <code>644</code></h3>
<p>The same permissions are written compactly as an octal number. Each digit stands for one of the three sets, and it&rsquo;s the sum of: <strong>read = 4</strong>, <strong>write = 2</strong>, <strong>execute = 1</strong>.</p>
<ul>
<li><code>7</code> = 4+2+1 = <code>rwx</code> (everything)</li>
<li><code>6</code> = 4+2 = <code>rw-</code> (read + write)</li>
<li><code>5</code> = 4+1 = <code>r-x</code> (read + execute)</li>
<li><code>4</code> = <code>r--</code> (read only)</li>
</ul>
<p>That makes the two most common patterns fall out on their own: <strong><code>644</code></strong> (<code>rw-r--r--</code>) for normal files and <strong><code>755</code></strong> (<code>rwxr-xr-x</code>) for directories and programs. <code>stat</code> shows both notations side by side:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">stat -c <span class="s1">&#39;%A %a %U %G %n&#39;</span> file.txt folder</span></span></code></pre></div>
</div>
<div class="sk-code">
  <span class="sk-code-head">Ausgabe</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">-rw-r--r-- 644 root root file.txt
</span></span><span class="line"><span class="cl">drwxr-xr-x 755 root root folder</span></span></code></pre></div>
</div>
<h3 id="step-5-setting-permissions-with-chmod">Step 5: Setting permissions with <code>chmod</code></h3>
<p><code>chmod</code> changes permissions – either numerically or symbolically. Numerically you set all three sets at once. This makes a file readable only by owner and group:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">chmod <span class="m">640</span> file.txt
</span></span><span class="line"><span class="cl">stat -c <span class="s1">&#39;%A %a %n&#39;</span> file.txt</span></span></code></pre></div>
</div>
<div class="sk-code">
  <span class="sk-code-head">Ausgabe</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">-rw-r----- 640 file.txt</span></span></code></pre></div>
</div>
<p>Symbolically you flip individual bits: <code>u</code> (user/owner), <code>g</code> (group), <code>o</code> (others), <code>a</code> (all), with <code>+</code>/<code>-</code>/<code>=</code>:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">chmod u+x,g-r file.txt
</span></span><span class="line"><span class="cl">stat -c <span class="s1">&#39;%A %a %n&#39;</span> file.txt</span></span></code></pre></div>
</div>
<div class="sk-code">
  <span class="sk-code-head">Ausgabe</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">-rwx------ 700 file.txt</span></span></code></pre></div>
</div>
<div class="not-prose my-6 rounded-lg border-l-4 p-4 border-amber-400 bg-amber-50 dark:border-amber-700 dark:bg-amber-900/20">
  <p class="mb-1 flex items-center gap-2 font-semibold text-slate-900 dark:text-white">
    <span aria-hidden="true">⚠️</span>Never chmod 777
  </p>
  <div class="prose-kitchen text-sm"><code>chmod 777</code> gives <em>everyone</em> on the system full write access – a classic beginner move to quickly &ldquo;make a permission problem go away&rdquo;. It&rsquo;s almost always the wrong fix and a real security hole. The right answer is to set the correct <strong>owner</strong> (step 6), not to weaken the permissions.</div>
</div>
<h3 id="step-6-transferring-ownership-with-chown">Step 6: Transferring ownership with <code>chown</code></h3>
<p>Who owns a file is changed with <code>chown</code> – in the format <code>user:group</code>:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">chown cook:cook file.txt
</span></span><span class="line"><span class="cl">stat -c <span class="s1">&#39;%A %a %U %G %n&#39;</span> file.txt</span></span></code></pre></div>
</div>
<div class="sk-code">
  <span class="sk-code-head">Ausgabe</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">-rwx------ 700 cook cook file.txt</span></span></code></pre></div>
</div>
<p>For a whole directory tree add <code>-R</code> (recursive) – exactly what you need constantly with Docker volumes when a container has to write as a specific user:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">chown -R cook:cook /opt/myapp/data</span></span></code></pre></div>
</div>
<p>What happens when the permissions are missing? Opening a root-only file as <code>cook</code>:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">su - cook -c <span class="s2">&#34;cat /tmp/secret.txt&#34;</span></span></span></code></pre></div>
</div>
<div class="sk-code">
  <span class="sk-code-head">Ausgabe</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">cat: /tmp/secret.txt: Permission denied</span></span></code></pre></div>
</div>
<p>That message is the most common permission error of all – now you know it doesn&rsquo;t mean something is broken, but that the model is working exactly as designed.</p>
<h3 id="step-7-groups--shared-access">Step 7: Groups – shared access</h3>
<p>Groups bundle users who should access the same files. A user is added to another group with <code>usermod -aG</code> (append to group):</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">usermod -aG docker cook
</span></span><span class="line"><span class="cl">id cook</span></span></code></pre></div>
</div>
<div class="sk-code">
  <span class="sk-code-head">Ausgabe</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">uid=1000(cook) gid=1000(cook) groups=1000(cook),990(docker)</span></span></code></pre></div>
</div>
<p>The <code>-a</code> is crucial: without <code>-a</code>, <code>usermod -G</code> <strong>replaces</strong> all secondary groups instead of adding one – that&rsquo;s how people accidentally drop out of <code>sudo</code> or <code>docker</code>. Two groups show up constantly:</p>
<ul>
<li><strong><code>sudo</code></strong> – members may run commands as root by prefixing <code>sudo</code>.</li>
<li><strong><code>docker</code></strong> – members may control Docker. Handy, but security-relevant: access to the Docker socket is effectively root access (see the warning below).</li>
</ul>
<div class="not-prose my-6 rounded-lg border-l-4 p-4 border-amber-400 bg-amber-50 dark:border-amber-700 dark:bg-amber-900/20">
  <p class="mb-1 flex items-center gap-2 font-semibold text-slate-900 dark:text-white">
    <span aria-hidden="true">⚠️</span>A new group is active only after a new login
  </p>
  <div class="prose-kitchen text-sm">A freshly assigned group only applies in a <strong>new session</strong>. After <code>usermod -aG</code> you must log out and back in (or start <code>newgrp docker</code>), otherwise <code>id</code> shows the group but <code>docker ps</code> keeps failing with &ldquo;permission denied&rdquo;.</div>
</div>
<h3 id="step-8-sudo-instead-of-permanent-root">Step 8: <code>sudo</code> instead of permanent root</h3>
<p>Staying logged in as root is dangerous: one typo wipes half the system, and any compromised process runs with full privileges. The safe way is a normal user in the <code>sudo</code> group who elevates individual commands when needed:</p>
<div class="sk-code">
  <span class="sk-code-head">Terminal</span>
  <div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">usermod -aG sudo cook</span></span></code></pre></div>
</div>
<p>After that, <code>cook</code> runs administrative commands by prefixing <code>sudo</code> (<code>sudo apt update</code>) and is asked for their <em>own</em> password. This is traceable (every action lands in <code>/var/log/auth.log</code>), reversible and far safer than a permanent root login. This is exactly what <a href="/en/tutorials/harden-ssh/">Harden SSH</a> builds on when it disables direct root login.</p>
<h2 id="when-things-go-wrong">When things go wrong</h2>
<div class="troubleshoot not-prose">
<p><strong><code>Permission denied</code> even though the permissions look right.</strong> Check the permissions of the <em>parent</em> directory: if it&rsquo;s missing the <code>x</code> bit, you can&rsquo;t reach the file at all, no matter what its own permissions are. <code>ls -ld /path/to/folder</code> shows it.</p>
<p><strong><code>docker ps</code> still says &ldquo;permission denied&rdquo; after <code>usermod -aG docker</code>.</strong> The new group isn&rsquo;t active in the current session yet – log out and back in (see the warning box in step 7). <code>id</code> in the <em>new</em> session must show <code>docker</code>.</p>
<p><strong>New files have unexpected permissions.</strong> That&rsquo;s set by the <code>umask</code> – it subtracts permissions from the defaults. The Debian default <code>umask 0022</code> produces <code>644</code> for files and <code>755</code> for directories. Check it with the <code>umask</code> command (no argument).</p>
<p><strong>A container won&rsquo;t write to the volume.</strong> The process inside the container runs under a specific UID (often not root). Set the owner of the host directory to match: <code>chown -R 1000:1000 ./data</code> – the image&rsquo;s docs name the correct UID (environment variables like <code>PUID</code>/<code>PGID</code>).</p>
<p><strong><code>usermod</code> dropped the user from groups.</strong> You used <code>-G</code> without <code>-a</code>. <code>-G</code> <em>replaces</em> the secondary groups. Always use <code>usermod -aG</code>. Repair: re-add the missing groups with <code>usermod -aG group1,group2 user</code>.</p>

</div>

<h2 id="maintenance--backups">Maintenance &amp; backups</h2>
<ul>
<li><strong>Permissions rarely need maintenance, but an audit pays off.</strong> Occasionally look for world-writable files – a common security leak: <code>find /opt -perm -0002 -type f</code>. Defuse any hits with <code>chmod o-w</code>.</li>
<li><strong>The Docker socket is root-equivalent.</strong> <code>ls -l /var/run/docker.sock</code> shows ownership <code>root:docker</code> – anyone in the <code>docker</code> group can start containers with the host filesystem mounted and thereby read and write everything. Only add trusted accounts to that group.</li>
<li><strong>Backups need to carry the permissions along.</strong> When you back up data, make sure ownership and permissions are preserved – <code>restic</code> and <code>rsync -a</code> do this automatically. Otherwise everything belongs to root after a restore and services won&rsquo;t start. How to do it cleanly is shown in <a href="/en/tutorials/restic-backups/">Backups with Restic</a>.
</content></li>
</ul>
]]></content:encoded></item></channel></rss>