How to Set Up Fedora Server

Complete Post-Install Configuration

After installing Fedora Server, complete essential post-install steps including updates, hostname, firewall, SSH keys, NTP, and optional role configuration.

The fresh install silence

You just finished the Fedora Server installer. The console drops you to a root prompt or a fresh user login. The system boots, but it is not ready for production. A default install leaves the firewall open to nothing, relies on password authentication by default, and runs on the exact kernel snapshot that shipped on the ISO. Deploying it as-is invites brute-force attempts, leaves you behind on security patches, and makes troubleshooting a guessing game. You need to lock the doors, set the clock, and establish a repeatable baseline before you install a single service.

What a default Fedora Server actually ships with

Fedora Server ships with a minimal footprint. It includes the tools you need to manage the system, but it deliberately leaves configuration decisions to you. The package manager tracks updates, but it does not apply them automatically. The firewall daemon runs, but it starts with a restrictive default zone that blocks everything except established connections. Time synchronization uses chrony, which handles leap seconds and drift better than the older ntpd, but you still need to verify it is tracking a reliable source. SELinux is enabled in enforcing mode out of the box, which means the kernel will silently block unauthorized process transitions until you audit the policy. Understanding these defaults prevents you from fighting the system later.

Hardening the base system

Start with the package manager. The ISO you booted from is already outdated the moment you finish the installation. Run a full refresh to pull the latest metadata and apply every available patch.

sudo dnf upgrade --refresh -y
# --refresh forces dnf to ignore cached metadata and fetch fresh repo data
# -y skips the interactive confirmation prompt for a clean run
# dnf upgrade --refresh is the normal weekly maintenance command

Check if a new kernel landed in the transaction. Fedora packages kernel updates alongside core libraries, and a reboot is required to switch the running kernel to the new version.

sudo systemctl reboot

Next, assign a permanent hostname. The installer may have left it as localhost.localdomain or a generic server. A proper FQDN matters for certificate generation, log aggregation, and service discovery.

sudo hostnamectl set-hostname srv01.example.com
# hostnamectl writes to /etc/hostname and updates the systemd transient name
# This avoids mismatched certificates and broken mail loops later

Verify that time synchronization is active. chrony handles network time protocol traffic and compensates for virtual machine clock drift.

sudo systemctl enable --now chronyd
# enable ensures the service starts on boot, --now starts it immediately
chronyc tracking
# tracking shows the current source, offset, and stratum level

Configure the firewall. firewalld uses zones to group interfaces and services. The default public zone drops incoming traffic unless you explicitly allow it. Open only the ports your server actually needs.

sudo systemctl enable --now firewalld
sudo firewall-cmd --permanent --add-service=ssh
# --permanent writes to the configuration file on disk
# runtime rules vanish after a reload or reboot
sudo firewall-cmd --reload
# reload applies the permanent rules to the active firewall state
# firewall-cmd --reload after every rule change prevents config divergence

Add additional services as you deploy them. Never leave the firewall in a permissive state while testing. If a service fails to bind, check the firewall rules before assuming the application is broken.

Reboot before you debug. Half the time the symptom is gone.

Securing remote access

Password authentication is the default on Fedora Server. That is fine for a local console, but it is a liability over the network. Switch to SSH key authentication and disable password logins. Generate a key pair on your workstation if you do not have one already.

ssh-copy-id youruser@srv01.example.com
# copies your local ~/.ssh/id_ed25519.pub to the remote ~/.ssh/authorized_keys
# prompts for your password one last time to establish the trust anchor

Log into the server and modify the SSH daemon configuration. Edit the file in /etc/ssh/sshd_config. Do not edit files in /usr/lib/. Those ship with the package and get overwritten on every update.

# /etc/ssh/sshd_config
PasswordAuthentication no
# disables password logins entirely
PubkeyAuthentication yes
# ensures key-based authentication remains active
PermitRootLogin no
# forces users to log in as their own account and use sudo

Restart the daemon to apply the changes. Keep your current terminal session open until you verify the new session works.

sudo systemctl restart sshd
# restarts the service without dropping existing connections

Enable automatic security updates. Fedora releases point updates frequently. dnf-automatic handles background downloads and reboots when necessary.

sudo dnf install -y dnf-automatic
# installs the timer and service units for unattended updates
sudo systemctl enable --now dnf-automatic-install.timer
# schedules daily checks for security and bugfix updates

Edit /etc/dnf/automatic.conf and set apply_updates = yes. The timer runs a daily check, downloads packages, and applies them. It will reboot the system if a kernel update requires it. Review the configuration carefully before enabling automatic reboots on a production workload.

Trust the package manager. Manual file edits drift, snapshots stay.

Managing access controls and dashboards

SELinux runs in enforcing mode by default. Do not disable it. It provides mandatory access control that limits what processes can read, write, or execute. If a service fails to start after you enable it, check the audit logs before changing the SELinux mode.

getenforce
# returns Enforcing, Permissive, or Disabled
sudo setenforce 1
# switches to enforcing mode at runtime without a reboot
sudo sed -i 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config
# ensures the policy persists across reboots

SELinux denials appear in the journal with a one-line summary. Read those before you disable the policy. The setroubleshoot package translates raw audit messages into actionable advice.

journalctl -t setroubleshoot | tail -n 20
# filters journal entries tagged by the SELinux troubleshooter
# shows recent denials and suggested fixes
# journalctl -xe reads better than journalctl alone for daily triage

Install Cockpit if you prefer a browser-based dashboard. It provides a terminal, service management, and basic system monitoring without requiring you to memorize command-line flags.

sudo dnf install -y cockpit
sudo systemctl enable --now cockpit.socket
# socket activation starts the web server only when a client connects
sudo firewall-cmd --permanent --add-service=cockpit
sudo firewall-cmd --reload

Access the dashboard at https://<server-ip>:9090. Cockpit uses your local system credentials and sudo privileges. It does not create a separate admin account.

If the boot menu is gone, GRUB rescue is your friend, not your enemy.

Verify the baseline

Run a quick health check before you deploy workloads. The package manager can verify dependency consistency, systemd can list failed units, and the journal can surface recent errors.

sudo dnf check
# scans the installed database for broken dependencies or conflicts
systemctl --failed
# lists any systemd units that failed to start during boot
journalctl -p err -b
# shows error-level messages from the current boot cycle

Review the output. A clean run returns nothing or shows only informational warnings. If systemctl --failed lists a unit, check its status before restarting it.

systemctl status <unit-name>
# shows recent log lines, active state, and dependency tree
# always check status before restart to avoid masking the root cause

Run journalctl first. Read the actual error before guessing.

Common pitfalls and error patterns

The most common mistake is editing configuration files in the wrong directory. Fedora splits package defaults and user overrides. Files in /usr/lib/ belong to the package maintainer. Files in /etc/ belong to the administrator. Edit /etc/. Changes in /usr/lib/ disappear on the next dnf upgrade.

Another frequent issue is firewall rule divergence. Adding a rule with firewall-cmd --add-service=http applies it to the runtime configuration only. The rule vanishes after a reboot. Always pair runtime tests with --permanent flags, then run firewall-cmd --reload. Otherwise the runtime config and the persistent config diverge, and you will spend hours wondering why a port is closed.

SELinux blocks are another trap. You will see errors like Permission denied or Connection refused when a service tries to bind to a port or read a custom directory. The error message rarely mentions SELinux directly. Check journalctl -t setroubleshoot or run ausearch -m avc -ts recent to find the actual denial. Create a custom policy or adjust the context instead of switching to permissive mode.

SSH key setup fails when the ~/.ssh directory has incorrect permissions. The SSH daemon refuses to read authorized_keys if the directory is world-readable.

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
# tightens permissions so sshd accepts the key file

Snapshot the system before the upgrade. Future-you will thank you.

Choosing your management path

You now have a hardened baseline. How you manage it depends on your workload and team size.

Use Cockpit when you want a visual dashboard for monitoring services, managing users, and accessing a web terminal. Use dnf-automatic when you run a small fleet of servers and want unattended security patches without manual intervention. Use manual dnf upgrade --refresh when you need to review changelogs, test updates in a staging environment, or maintain strict change control. Use SSH key authentication when you are accessing the server over the internet or from untrusted networks. Use password authentication only when you are working on an isolated local console or a temporary test box. Stay on the default SELinux enforcing mode when you are running standard services like nginx, PostgreSQL, or Docker. Switch to permissive mode only when you are debugging a custom application that requires non-standard file access. Plan upgrades on the six-month cycle. The N-2 release goes EOL when N+1 ships.

Where to go next