---
title: "Remove services the server does not need · Agent Wranglers"
url: https://agentwranglers.se/playbook/infrastructure/servers/minimal-services
updated: 2026-10-21
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Remove services the server does not need

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers). Updated 2026-10-21.

For AI agents: [llms.txt](https://agentwranglers.se/llms.txt) is a short guide that sends you to the rules your problem needs, and [rules.json](https://agentwranglers.se/playbook/infrastructure/rules.json) holds every rule of this playbook as JSON.

- Level: Recommended
- Chapter: [Servers](https://agentwranglers.se/playbook/infrastructure/servers)
- Rule: Install only the packages and services the server's job needs. Remove the rest, and disable what cannot be removed.
- How: Start from the smallest image of the system, such as a minimal cloud image. Remove compilers, mail servers, file sharing and admin tools the service does not use. Removing beats disabling, because a disabled service can be turned on again by mistake or by an attacker. Keep the list of expected listening ports in code, next to the [host firewall](https://agentwranglers.se/playbook/infrastructure/servers/host-firewall) rules. The same idea keeps build tools out of container images, as [Keep build tools out of the runtime image](https://agentwranglers.se/playbook/operations/containers/multi-stage-builds) says.
- Check: The script below prints nothing on any server. It compares the listening ports with the expected list kept in code.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### Shell example

```sh
# Expected listening ports for this server, from the server's code. Prints every unexpected one.
EXPECTED="22 80 443"
ss -Htuln | awk '{ n = split($5, a, ":"); print a[n] }' | sort -un | while read -r port; do
  case " $EXPECTED " in *" $port "*) ;; *) echo "unexpected port $port" ;; esac
done
```

### Shell example 2

```sh
apt-get purge --auto-remove -y postfix rpcbind cups avahi-daemon   # Debian and Ubuntu
dnf remove -y postfix rpcbind cups avahi                           # Red Hat family
systemctl list-unit-files --state=enabled --type=service           # compare with the server's list
```

## Sources

- [NIST SP 800-123 Guide to General Server Security](https://csrc.nist.gov/pubs/sp/800/123/final)

## Related rules

- [Turn on a firewall on every host](https://agentwranglers.se/playbook/infrastructure/servers/host-firewall) in Infrastructure
- [Keep build tools out of the runtime image](https://agentwranglers.se/playbook/operations/containers/multi-stage-builds) in Operations

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/infrastructure/servers/minimal-services">Agent Wranglers infrastructure playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Keep server clocks in sync](https://agentwranglers.se/playbook/infrastructure/servers/time-sync)
- Next: [Check server images against a hardening checklist](https://agentwranglers.se/playbook/infrastructure/servers/hardening-checklist)
- [Servers](https://agentwranglers.se/playbook/infrastructure/servers)
