firewalld on openSUSE: zones, services and ports
Read the current rules, open a service or port the permanent way, and limit it to the networks that need it.
Part 6 of 10 in openSUSE home server setup. Last updated
In short: firewalld sorts interfaces and source addresses into zones; public is the default on openSUSE. Check with sudo firewall-cmd --get-active-zones and --list-all. Open a service with sudo firewall-cmd --permanent --zone=public --add-service=http, then sudo firewall-cmd --reload; without --permanent the change is gone after a reload or reboot. Limit a service to your LAN by giving the LAN subnet its own zone. Rootless container ports behave like any program and need a firewalld rule; test rootful container ports from another machine.
Zones in one paragraph
A zone is a trust level with its own list of allowed services. Every network interface (and
optionally every source subnet) belongs to one zone, and traffic arriving there gets that
zone's rules. openSUSE puts interfaces in public unless told otherwise. So "open
port 8080" really means "allow 8080 in the zone my interface is in".
What is open right now?
sudo firewall-cmd --state # running
sudo firewall-cmd --get-active-zones # zones with their interfaces and sources
sudo firewall-cmd --list-all # services and ports of the default zone
sudo firewall-cmd --zone=public --list-all # the same for a named zone
sudo firewall-cmd --info-service=cockpit # which ports a service name stands for
Zones and services are plain XML files you can read without root. The shipped
public zone and the cockpit service:
$ cat /usr/lib/firewalld/zones/public.xml
<?xml version="1.0" encoding="utf-8"?>
<zone>
<short>Public</short>
<description>For use in public areas. You do not trust the other computers on networks to not harm your computer. Only selected incoming connections are accepted.</description>
<service name="ssh"/>
<service name="dhcpv6-client"/>
<forward/>
</zone>
$ cat /usr/lib/firewalld/services/cockpit.xml
<?xml version="1.0" encoding="utf-8"?>
<service>
<short>Cockpit</short>
<description>Cockpit lets you access and configure your server remotely.</description>
<port protocol="tcp" port="9090"/>
</service> Real output on openSUSE Tumbleweed, firewalld, 2026-09-28; user and host names and addresses redacted.
$ ls /usr/lib/firewalld/zones/ | tr '\n' ' '
block.xml dmz.xml docker.xml drop.xml external.xml home.xml internal.xml nm-shared.xml public.xml trusted.xml work.xml
$ ls /usr/lib/firewalld/services/ | wc -l
263 Real output on openSUSE Tumbleweed, firewalld, 2026-09-28; user and host names and addresses redacted.
Your own changes live in /etc/firewalld/ and override these; never edit the files
under /usr/lib.
Open a service or a port
Prefer a service name when one exists; it documents itself. All changes need root:
sudo firewall-cmd --permanent --zone=public --add-service=http
sudo firewall-cmd --permanent --zone=public --add-port=8080/tcp
sudo firewall-cmd --reload # apply the permanent config
sudo firewall-cmd --zone=public --list-all # check
# undo:
sudo firewall-cmd --permanent --zone=public --remove-port=8080/tcp
sudo firewall-cmd --reload --permanent writes the saved config but does not touch the running firewall;
--reload loads the saved config. firewall-cmd --get-services lists
every known service name. The firewalld port
helper builds these commands for you, including the check and the undo.
Only from your LAN
A dashboard should answer your LAN, not every device that reaches the server. Put the LAN subnet in its own zone and open the service there only:
sudo firewall-cmd --permanent --zone=home --add-source=192.168.1.0/24
sudo firewall-cmd --permanent --zone=home --add-port=3000/tcp
sudo firewall-cmd --reload
A source match wins over the interface zone, so packets from 192.168.1.x get the
home rules and everything else stays in public. Check what
home already allows with --zone=home --list-all; it opens a few
services by default. The same idea with a WireGuard interface is in the
Cockpit over WireGuard guide.
firewalld and podman
- Rootless containers: a published port (
PublishPort=8080:80) is a normal socket owned by your user. firewalld treats it like any program: open the port, or it is only reachable from the server itself. - Rootful containers: netavark adds its own forwarding rules for published
ports, and depending on the setup a port can be reachable even though firewalld does not
list it. Do not trust
--list-allhere: test from another machine (curl http://server:8080ornc -zv server 8080), and bind to a specific address (PublishPort=127.0.0.1:8080:80) when a port should stay local.
Rootless podman is the default in this series; the rootless podman guide explains why.
Common mistakes
Forgetting --permanent
The port works until the next reboot, then mysteriously closes. If you tested at runtime,
save the result with sudo firewall-cmd --runtime-to-permanent.
Opening the port in a zone the interface is not in
--add-port without --zone uses the default zone. If your interface
sits in another zone, nothing changes for it. --get-active-zones tells you
which zone to use.
Removing ssh from the zone you are connected through
The next reload ends your session. Keep a console at hand when changing the rules for the zone that carries your own SSH connection.
Frequently asked questions
Does firewall-cmd need root?
Changes always do. Some read-only queries work for normal users through polkit, but running every firewall-cmd with sudo on a server avoids surprises.
What is the difference between --permanent and runtime?
Without --permanent, firewall-cmd changes the running firewall only; a reload or reboot forgets it. With --permanent it changes the saved configuration only, which takes effect after sudo firewall-cmd --reload. Trying runtime first and saving with --runtime-to-permanent is a safe way to test.
Should I use ufw on openSUSE?
No. openSUSE ships and integrates firewalld; ufw would be a second firewall manager fighting over the same nftables rules.