<?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>Ufw – Serverküche</title><link>https://serverkueche.de/en/tags/ufw/</link><description>Ufw – 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>Fri, 17 Jul 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://serverkueche.de/en/tags/ufw/index.xml" rel="self" type="application/rss+xml"/><item><title>Setting up a firewall with UFW</title><link>https://serverkueche.de/en/tutorials/firewall-ufw-setup/</link><pubDate>Fri, 17 Jul 2026 00:00:00 +0000</pubDate><author>feedback@serverkueche.de (Serverküche)</author><guid>https://serverkueche.de/en/tutorials/firewall-ufw-setup/</guid><description>Set up a host firewall in minutes with UFW: block everything except SSH, HTTP and HTTPS on your server – without ever locking yourself out.</description><content:encoded><![CDATA[<p>By default, your server accepts connections on all open ports. A <strong>firewall</strong>
turns that around: it blocks everything and only lets through what you explicitly
allow. <strong>UFW</strong> (&ldquo;Uncomplicated Firewall&rdquo;) does this with a few, readable commands –
the perfect first line of defense.</p>
<h2 id="what-are-we-building">What are we building?</h2>
<p>By the end, <strong>UFW</strong> runs on your <strong>Debian 13</strong> with the base rule &ldquo;<strong>block all
incoming</strong>&rdquo;, with only <strong>SSH</strong> (your access) plus <strong>HTTP/HTTPS</strong> (ports 80/443,
which you&rsquo;ll need later for Traefik) open. Outgoing connections stay allowed. The
key part: we proceed in a way that ensures you <strong>don&rsquo;t lock yourself out</strong>.</p>
<h2 id="prerequisites">Prerequisites</h2>
<ul>
<li>A <a href="/en/tutorials/harden-ssh/">hardened server</a> with a sudo user</li>
<li>You know which <strong>port your SSH</strong> runs on (default: 22)</li>
</ul>
<h2 id="step-by-step">Step by step</h2>
<h3 id="step-1-install-ufw">Step 1: Install UFW</h3>
<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">sudo apt update
</span></span><span class="line"><span class="cl">sudo apt install -y ufw</span></span></code></pre></div>
</div>
<p>Check the version – UFW is <strong>inactive</strong> at first, which is intended:</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">ufw version</span></span></code></pre></div>
</div>
<h3 id="step-2-define-the-base-rules">Step 2: Define the base rules</h3>
<p>First the default direction: deny everything incoming, allow everything outgoing.</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">sudo ufw default deny incoming
</span></span><span class="line"><span class="cl">sudo ufw default allow outgoing</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">Default incoming policy changed to &#39;deny&#39;
</span></span><span class="line"><span class="cl">(be sure to update your rules accordingly)
</span></span><span class="line"><span class="cl">Default outgoing policy changed to &#39;allow&#39;
</span></span><span class="line"><span class="cl">(be sure to update your rules accordingly)</span></span></code></pre></div>
</div>
<p>These rules don&rsquo;t take effect yet – UFW is still inactive. So the exceptions come
now, <strong>before</strong> we switch it on.</p>
<h3 id="step-3-allow-ssh--first-or-youll-lock-yourself-out">Step 3: Allow SSH – first, or you&rsquo;ll lock yourself out</h3>
<div class="not-prose my-6 rounded-lg border-l-4 p-4 border-paprika-400 bg-paprika-50 dark:border-paprika-700 dark:bg-paprika-900/20">
  <p class="mb-1 flex items-center gap-2 font-semibold text-slate-900 dark:text-white">
    <span aria-hidden="true">🔥</span>Allow SSH first, then activate
  </p>
  <div class="prose-kitchen text-sm">If you enable UFW with the &ldquo;deny incoming&rdquo; rule <strong>without</strong> having allowed SSH
first, the next command cuts your own connection – and you can no longer get in via
SSH. This order is non-negotiable.</div>
</div>
<p>If your SSH runs on the default port 22 (and <code>openssh-server</code> is installed), there&rsquo;s
a ready-made profile for it:</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">sudo ufw allow OpenSSH</span></span></code></pre></div>
</div>
<p>If you changed the SSH port (e.g. to 2222), allow exactly that port instead:</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">sudo ufw allow 2222/tcp</span></span></code></pre></div>
</div>
<h3 id="step-4-allow-http-and-https">Step 4: Allow HTTP and HTTPS</h3>
<p>For the later <a href="/en/tutorials/traefik-reverse-proxy/">reverse proxy with Traefik</a> you
need the web ports. Open them right away:</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">sudo ufw allow 80/tcp
</span></span><span class="line"><span class="cl">sudo ufw allow 443/tcp</span></span></code></pre></div>
</div>
<p>UFW confirms each rule with <code>Rules updated</code> and <code>Rules updated (v6)</code> (the IPv6
variant).</p>
<h3 id="step-5-activate-the-firewall-and-check-it">Step 5: Activate the firewall and check it</h3>
<p>Now switch it on:</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">sudo ufw enable</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">Command may disrupt existing ssh connections. Proceed with operation (y|n)? y
</span></span><span class="line"><span class="cl">Firewall is active and enabled on system startup</span></span></code></pre></div>
</div>
<p>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">sudo ufw status verbose</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">Status: active
</span></span><span class="line"><span class="cl">Logging: on (low)
</span></span><span class="line"><span class="cl">Default: deny (incoming), allow (outgoing), disabled (routed)
</span></span><span class="line"><span class="cl">New profiles: skip
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">To                         Action      From
</span></span><span class="line"><span class="cl">--                         ------      ----
</span></span><span class="line"><span class="cl">22/tcp (OpenSSH)           ALLOW IN    Anywhere
</span></span><span class="line"><span class="cl">80/tcp                     ALLOW IN    Anywhere
</span></span><span class="line"><span class="cl">443/tcp                    ALLOW IN    Anywhere
</span></span><span class="line"><span class="cl">22/tcp (OpenSSH (v6))      ALLOW IN    Anywhere (v6)
</span></span><span class="line"><span class="cl">80/tcp (v6)                ALLOW IN    Anywhere (v6)
</span></span><span class="line"><span class="cl">443/tcp (v6)               ALLOW IN    Anywhere (v6)</span></span></code></pre></div>
</div>
<p><code>Status: active</code> and your three allowances – done. <strong>To be safe, test in a second
SSH session</strong> that you can still connect before closing the first one.</p>
<h2 id="when-things-go-wrong">When things go wrong</h2>
<div class="troubleshoot not-prose">
<p><strong>After <code>ufw enable</code> you can no longer get in via SSH.</strong> The SSH rule was missing or
targeted the wrong port. Connect via the <strong>console in the netcup SCP</strong> (VNC,
independent of SSH), allow your SSH port there (<code>sudo ufw allow …</code>) and test again.
That&rsquo;s exactly what step 3 warns about.</p>
<p><strong><code>ERROR: Could not find a profile matching 'OpenSSH'</code>.</strong> The OpenSSH profile only
exists if <code>openssh-server</code> is installed. Use the port number instead: <code>sudo ufw allow 22/tcp</code> (or your port). <code>sudo ufw app list</code> shows the available profiles.</p>
<p><strong>A Docker container is reachable from outside even though UFW doesn&rsquo;t open the
port.</strong> Not your mistake – <strong>Docker bypasses UFW</strong>. Docker writes its rules directly
into <code>iptables</code> and inserts them ahead of the UFW chains. A published container port
(<code>ports:</code> in the compose.yaml) is thus open, no matter what UFW says. The clean
solution is a second firewall layer <strong>in front of</strong> the server – at netcup the
<a href="/en/tutorials/netcup-firewall-setup/">firewall in the SCP</a> as an upstream
perimeter. On the host it also helps to bind container ports only to <code>127.0.0.1</code>
instead of <code>0.0.0.0</code>.</p>

</div>

<h2 id="maintenance--backups">Maintenance &amp; backups</h2>
<ul>
<li><strong>View and delete rules:</strong> <code>sudo ufw status numbered</code> numbers all rules;
<code>sudo ufw delete 3</code> removes rule 3. That&rsquo;s how you tidy up when a service goes
away.</li>
<li><strong>New services:</strong> For every additional public port, one targeted rule – never
&ldquo;just open everything&rdquo;. What doesn&rsquo;t need to be open stays closed.</li>
<li><strong>No backup needed,</strong> but note down your allowances (or keep them in the same
document as your DNS records). The rule set is retyped in seconds.</li>
<li><strong>Know the limits:</strong> UFW protects the host, but not against the Docker gap above.
An upstream perimeter firewall like the
<a href="/en/tutorials/netcup-firewall-setup/">netcup firewall in the SCP</a> complements UFW
into two layers that secure each other – ideal once containers with open ports are
running.</li>
</ul>
]]></content:encoded></item></channel></rss>