On most long-running Linux servers, UFW rules don’t get removed; they get forgotten. Services change, ports shift, packages come and go, and the firewall stops matching what the box is actually doing. You only notice when you audit it, or when something breaks and nobody remembers why a port was ever opened.
UFW Application Profiles sit in that gap. They don’t secure anything on their own, and they don’t track running services, but they do give structure to how ports get opened and why. Used carefully, they make firewall rules easier to reason about months later. Used casually, they hide assumptions and create blind spots.
This isn’t about learning UFW or opening your first port. It’s about how application profiles behave on real servers that stay up for years, how to read what they actually allow, and how to create your own without surprises.
Most systems ship with an OpenSSH application profile, which makes it a useful reference point.
When you run:
ufw allow OpenSSHUFW is not doing anything SSH-specific. It looks up the OpenSSH profile definition, usually stored under /etc/ufw/applications.d/, reads the ports and protocols listed there, and expands them into standard firewall rules. On most systems, that means allowing TCP port 22. The profile name itself never appears in the packet path. It is resolved once, at rule creation time, and then disappears.
You can see exactly what UFW will expand by inspecting the profile directly:
ufw app info OpenSSHThis shows the ports and protocols the profile maps to. If SSH has been moved to a non-standard port, this output will not change unless the profile itself is updated.
After allowing the profile, UFW writes normal port-based rules. You can confirm that with:
ufw status verboseNotice that the output lists ports and protocols, not the application profile name. At this point, the firewall no longer knows or cares that the rule originated from OpenSSH.
This is why application profiles behave more like macros than active policy objects. The human-readable name exists for operators. The firewall only ever enforces ports and protocols.
It also explains several behaviors that confuse people later. If SSH moves to a different port, the profile does not follow it. If the profile file is deleted, the allow rule still exists. If IPv6 is enabled, the same ports are opened on both stacks. And if Docker rewrites iptables rules underneath UFW, the profile’s intent may never be enforced at all.
Understanding this single example makes the rest of UFW application profiles easier to reason about. Profiles document intent. UFW enforces ports. Once the rule is written, the profile name is no longer part of the system’s behavior, only its history.
At a high level, UFW Application Profiles are just a naming and grouping layer that UFW knows how to translate into firewall rules.
A UFW (Uncomplicated Firewall) Application Profile is a metadata abstraction that maps a human-readable name to one or more ports and protocols. It exists to make rules easier to read and reason about later, not to reflect what is actually running on the system. The profile only becomes meaningful when UFW expands it into rules, which is where its relationship to ufw in Linux really lives.
Profiles are not service-aware. They don’t watch processes, they don’t follow sockets, and they don’t change when a daemon moves or dies. They also don’t enforce policy on their own. Until a profile is referenced by an allow or deny rule, it’s just a definition sitting on disk.
What profiles control
What profiles do not control
On disk, profiles live under /etc/ufw/applications.d/. UFW reads these files at runtime, parses the definitions, and makes them available by name when you reference them in a rule. Simply having a file there does nothing by itself. Presence is not activation. Until a profile is explicitly used in a UFW rule, no firewall behavior changes.
This is where people get tripped up on long-lived systems. Files accumulate. Profiles stick around after services are removed. UFW doesn’t care. It will happily list profiles that no longer match reality unless you look closer.
When Profiles Help | When Profiles Hurt |
Standard services across hosts | Blind trust in defaults |
Multi-port services | Ports change, profiles don’t |
Readability and audits | Containers bypassing policy |
Shared naming across teams | Assuming profiles track services |
Docker deserves a specific callout here. Docker manipulates iptables directly, outside of UFW’s model. Application profiles document intent. They do not enforce it. If packets never hit UFW’s rules, the profile name won’t save you.
Reading a profile is about confirming what will open, not what the name suggests.
ufw app listThe list depends on the distribution and installed packages, so identical services across hosts can produce different results.
ufw app info This shows the ports and protocols the profile will translate into rules. Before applying it, confirm those ports match the service’s current configuration, not a default that changed during an upgrade.
Warning: UFW logs ports and protocols, not profile names. During an audit UFW rules review, you have to trace traffic back to the profile definition manually.
Profile definitions live in /etc/ufw/applications.d/. On long-lived systems, this directory often contains profiles for services that no longer exist or no longer use the same ports. UFW does not validate these files against running services, so file-level inspection is required to understand what a profile actually represents before trusting or reusing it.
Applying a profile is simple, but understanding what UFW actually does with it is where mistakes start.
When you allow a profile, UFW resolves the profile name into ports and protocols, then writes normal firewall rules for those values. The flow is named to port to rule, and once the rule is written, the profile name no longer exists anywhere in the packet path or logs.
Manual rules are not special-cased. A deny rule you added earlier will still block traffic even if a profile later allows the same port. Likewise, allowing a profile does not override an explicit deny. Profiles don’t change precedence. They just expand into rules like anything else.
When allow is appropriate
When limit makes sense
Rate limiting reduces connection abuse. It does not reduce exposure. Using limit does not change who can reach the service, which is often misunderstood when people treat it like a safety net for UFW allowlist services.
Profiles can be scoped the same way as any other rule. The restriction applies to the expanded rules, not the profile itself.
A common pattern is allowing a profile only from a trusted network while denying the same ports everywhere else.
Profiles apply to IPv6 the same way they do to IPv4. The same ports are opened, just on a different stack, and that surprises people during reviews.
Common pitfalls
If IPv6 is enabled in UFW, profiles expand into both stacks unless you’ve explicitly constrained them, which is where most unintended exposure comes from.
You usually end up writing custom profiles when the defaults stop lining up with how the service actually behaves.
When a service listens on ports that don’t match any shipped profile, you’re already outside the safe path. If the service is internal-only, reusing a public-facing default tends to blur intent and makes audits harder than they need to be.
The same applies when a single application exposes multiple ports that belong together. Splitting those across ad-hoc rules works at first, but it gets fragile fast. And once you need that same definition applied across a fleet, copying raw port rules stops scaling. That’s usually the point where a custom profile earns its keep.
[MyApp]
title=My internal service
description=Internal API listener
ports=8080/tcp|8443/tcp|9000:9010/udpField | Purpose | Example |
title | Human-readable name | My internal service |
description | Context for audits and ownership | Internal API listener |
ports | Allowed ports and protocols | 8080/tcp|9000:9010/udp |
UFW treats this file as a flat definition. The pipe operator | separates ports and ranges, and ranges use start:end syntax. You can list many ports, but in practice, once a profile grows past roughly 15 to 20 entries, it stops being readable and starts hiding mistakes.
The workflow is simple and intentionally boring. Create the profile file under /etc/ufw/applications.d/. Confirm UFW can see it. Apply it like any other rule. Then verify the resulting rules actually match the ports you expected to open. Skipping the last step is how profiles drift away from reality.
Automation changes the failure mode. UFW does not reload profile definitions automatically. After a profile file changes, you must explicitly refresh the rules, or nothing happens.
For existing profiles, ufw app update
Warning: automation also has to be idempotent. Reapplying profiles without checking existing rules can create duplicates or reorder rules, and that’s how a clean profile definition turns into a firewall state nobody trusts anymore.
Problems with application profiles usually surface during audits or outages, when rules no longer align with the system’s current state.
A few patterns recur in long-lived systems.
There’s no single command that tells you a profile is wrong. You have to line up a few signals.
If the profile, the rules, and the listeners don’t all agree, trust the listeners.
Changes need to be staged, not swapped.
This add-before-remove sequence avoids lockouts and leaves room to recover.
A few failure modes come up often enough that they’re worth calling out directly.
The Problem | What Actually Happens | Why It Matters |
Ghost rules | Profile file is removed, but the allow rules remain | Ports stay open with no visible source or intent |
Rules removed first | Rules are deleted before the profile file | You lose context about what was allowed and why |
Incorrect decommissioning order | Service or profile is removed before rules are cleaned up | Leftover rules accumulate and confuse audits |
Profiles and containers | Container runtimes bypass UFW via iptables | Profiles appear correct, but traffic ignores them |
Profiles not found | Rules reference ports from a profile that no longer exists | The rules still work, so the missing profile is easy to miss |
Unexpected exposure | Profiles open more ports than intended | Extra listeners or IPv6 widen access without notice |
Overlapping profiles | Multiple profiles expand to the same port | Removing one profile’s rule does not close the port |
These issues usually surface during debugging ufw firewall work, when intent, rules, and observed traffic finally get compared side by side.
In production, profiles work best when they reflect intent.
UFW Application Profiles behave the same at the firewall level on Debian and Ubuntu, but what each distribution ships by default can affect how profiles appear in practice.
The practical takeaway is to treat distro-shipped profiles as convenience defaults, not canonical definitions. For production or long-lived hosts, explicitly managed custom profiles are easier to audit and far more predictable.
These questions usually come up when reviewing firewall behavior on real systems.
UFW application profiles are named definitions that map to one or more ports and protocols. When applied, UFW expands the profile into standard firewall rules. Application profiles do not track running services or enforce policy by themselves.
UFW application profiles are stored in /etc/ufw/applications.d/. UFW reads these files when rules reference them, but profile files alone do not change firewall behavior.
UFW resolves application profile names into ports at rule creation time. After that point, logging reflects only ports and protocols, not profile names.
Deleting a UFW application profile file does not remove the firewall rules created from it. The port remains open until the corresponding UFW rules are explicitly removed.
UFW application profiles do not reliably control Docker traffic. Docker modifies iptables directly, which can bypass UFW rules and profile-based intent.
When IPv6 is enabled in UFW, application profiles open the same ports on IPv6 as on IPv4. This can expose services over IPv6 even when only IPv4 behavior was reviewed.
Task | Command |
List profiles | |
View profile | |
Apply profile | |
Remove rule | |