How to Troubleshoot Connection Issues Caused by firewalld on Fedora

Identify and fix firewalld-related connection problems on Fedora by checking active rules, zones, and logs, then adding the appropriate service or port exceptions.

You set up a service and the connection hangs

You installed a web server on Fedora. The service status shows active (running). You open a browser on another machine and the page never loads. The connection hangs until it times out. You check the IP address and it is correct. You check the port and it matches the configuration. The problem is firewalld. Fedora enables the firewall by default. It blocks all incoming connections unless you explicitly allow them. Your service is running, but the packets are being dropped before they reach the application.

What firewalld is doing

Firewalld acts as a gatekeeper for your network interfaces. It inspects every packet arriving at the system. It compares the packet against a set of rules defined for the zone assigned to that interface. If no rule matches, the default policy applies. The default policy for most zones is to reject or drop incoming traffic. The application never sees the packet. The application logs nothing because it never receives the request. The client sees a timeout. This behavior protects the system from unsolicited traffic. It also requires you to open ports for any service you want to expose to the network.

Understand zones and configurations

Firewalld uses zones to group interfaces by trust level. A zone defines the set of rules applied to the interfaces assigned to it. Fedora assigns interfaces to zones automatically based on network detection, or you can assign them manually.

The public zone is the default for most desktops and servers. It assumes the network is untrusted. It allows only essential services like SSH, DHCPv6, and mDNS by default. Everything else is blocked. The internal zone assumes the network is trusted, such as a home LAN. It allows more services by default. The trusted zone accepts all traffic. The drop zone drops all traffic without sending a reply.

An interface belongs to one zone at a time. Rules apply to the zone, not the interface directly. If you move an interface to a different zone, the rules change instantly. Check the zone assignment before adding rules. A rule added to the public zone does nothing if your interface is in the internal zone.

Firewalld maintains two configurations. The runtime configuration is active in memory. It changes immediately when you run commands without --permanent. These changes disappear on reboot. The permanent configuration is stored on disk. It loads when the system starts. Commands with --permanent modify only the disk file. They do not affect the running firewall. You must reload the firewall to apply permanent changes to the runtime. This separation prevents locking yourself out. If you make a mistake in the permanent config, the runtime config keeps working until you reload.

Service definitions live in /usr/lib/firewalld/services/. Do not edit files there. Package updates will overwrite your changes. Create custom service definitions in /etc/firewalld/services/ if you need to override defaults. Files in /etc/ persist across updates and take precedence over /usr/lib/.

Check the zone assignment first. A rule in the wrong zone does nothing.

Isolate and fix the block

Confirm firewalld is the blocker

Stop the firewall temporarily to see if the connection works. If the connection succeeds with the firewall stopped, firewalld is blocking the traffic. Restart the firewall immediately after testing. Never leave firewalld disabled on a machine connected to a network.

# Stop the firewall temporarily to isolate the issue
sudo systemctl stop firewalld
# Test your connection now. If it works, firewalld is blocking the traffic.
# Restart the firewall immediately after testing. Do not leave it off.
sudo systemctl start firewalld

Stop the firewall to test, start it to live. Leaving firewalld disabled exposes every port to the network.

Check the active zone and rules

Identify which zone your interface is using. List the rules for that zone to see what is currently allowed. The output shows services, ports, and rich rules. Compare this list with the service you are trying to reach.

# Show which interfaces are assigned to which zones
sudo firewall-cmd --get-active-zones
# List all rules for the public zone, including services and ports
sudo firewall-cmd --list-all --zone=public

Check the zone assignment first. A rule in the wrong zone does nothing.

Allow a service by name

Firewalld ships with pre-defined service definitions for common daemons. Use service names when available. Service definitions handle port numbers and protocols automatically. They also update if the standard port changes in future releases. Add the service to the permanent configuration and reload the firewall.

# Add the http service to the permanent configuration
sudo firewall-cmd --add-service=http --permanent
# Reload the firewall to apply permanent changes to the running configuration
# Skipping this step leaves the runtime config out of sync with the permanent config
sudo firewall-cmd --reload

Reload after every permanent change. The runtime config and persistent config diverge without it.

Allow a specific port

Use port rules when no service definition exists for your application. Specify the port number and protocol. TCP and UDP are separate protocols. You must open the port for each protocol the service uses. Add the port to the permanent configuration and reload.

# Open port 8080 for TCP traffic in the permanent configuration
sudo firewall-cmd --add-port=8080/tcp --permanent
# Reload to apply the change
sudo firewall-cmd --reload

Reload after every permanent change. The runtime config and persistent config diverge without it.

Enable logging for dropped packets

Enable logging to see which packets firewalld is dropping. The logs show the source IP, destination port, and interface. This information helps you write the correct rule. Use journalctl -xe to view the logs with explanatory text. The x flag adds context and the e flag jumps to the end.

# Enable logging for all denied packets
sudo firewall-cmd --set-log-denied=all
# View the journal with explanatory text and jump to the end
# The x flag adds context, the e flag jumps to recent entries
sudo journalctl -xe

Enable logging to see what is being dropped. Read the source IP and destination port before writing a rule.

Rich rules for fine-grained control

Rich rules allow complex matching. Use them when you need to restrict access by source IP, map ports, or limit bandwidth. Rich rules use XML-like syntax. Add the rule to the permanent configuration and reload.

# Allow HTTP only from the 192.168.1.0/24 subnet
# This rule applies to the permanent configuration
sudo firewall-cmd --add-rich-rule='rule family="ipv4" source address="192.168.1.0/24" service name="http" accept' --permanent
# Reload to apply
sudo firewall-cmd --reload

Reload after every permanent change. The runtime config and persistent config diverge without it.

Verify the fix

List the current runtime configuration to confirm the rule is active. Compare it with the permanent configuration to ensure they match. If they differ, a reboot will revert the runtime changes. Test the connection again.

# List the current runtime configuration to verify the rule is active
sudo firewall-cmd --list-all
# Compare with the permanent configuration to ensure they match
sudo firewall-cmd --list-all --permanent

Compare runtime and permanent configs. They must match, or a reboot will break your access.

Common pitfalls

The error message tells you where the packet stopped. Connection timed out means the client sent a request and got no response. The firewall is likely dropping the packet. Connection refused means the client got a rejection from the host. The firewall allowed the packet, but no process is listening on that port. Check the service status if you see Connection refused.

Verify the service is listening on the correct interface. Some services bind to 127.0.0.1 by default. They only accept connections from localhost. Use ss to check the listening address. The output shows the local address and port. If the address is 127.0.0.1, the service will not accept remote connections regardless of firewall rules.

# Check if the service is listening on the expected port and interface
# The -t flag shows TCP, -l shows listening, -n shows numeric ports, -p shows process
sudo ss -tlnp | grep :8080

Check the bind address. A service bound to localhost ignores the firewall.

SELinux can block services even when the firewall allows the port. If the port is open and the service is running, check SELinux logs. SELinux denials appear in journalctl -t setroubleshoot. The logs show a one-line summary of the denial. Read the summary before disabling SELinux. You often need to label the port or adjust the boolean.

Check SELinux if the port is open and the service is running. A firewall rule does not fix an SELinux denial.

Choose the right rule type

Use --add-service when the daemon has a standard port and a definition file in /usr/lib/firewalld/services/. Use --add-port when you are running a custom application on a non-standard port. Use rich rules when you need to restrict access by source IP or map ports. Use --zone=trusted only for internal interfaces where you want to allow all traffic without managing individual rules. Stay on the public zone for interfaces exposed to untrusted networks.

Where to go next