Installing software on a dedicated server is unrestricted, which is the point of the tier and also the risk. Every package you add is something to patch, something that may open a port, and something that will still be running in two years when nobody remembers why.
Use the distribution's packages
Install from the distribution repositories wherever possible. Those packages receive security updates through the same mechanism as everything else, which means automatic updates cover them.
Software installed from a vendor script, compiled from source, or pulled from a third-party repository does not. You have taken on the job of tracking its advisories and rebuilding it, and that job is invisible until an advisory appears.
That is a real cost, not a theoretical one. Choose it deliberately when you need a version the distribution does not ship, and know you have chosen it.
Check what a package started
Packages frequently start services you did not ask for, and some bind to a public address by default.
After installing anything, list what is listening and confirm nothing new is exposed. A database or cache reachable from the internet is found by automated scanning within hours, and a great many compromises begin exactly this way, not through the application, but through a service nobody knew was public.
Bind services to localhost unless something genuinely connects from another machine.
Run applications as their own user
Not as root, and not all as the same user.
A service running as root that is compromised gives away the machine. The same service running as a limited user gives away what that user can reach, which is a recoverable situation.
Same reasoning for databases: one user per application, with privileges on its own database only.
Containers change the trade
Running an application in a container isolates its dependencies from the host and from other applications, which genuinely helps when two things need conflicting versions.
It does not remove the patching obligation. Container images contain an operating system, and an image built eighteen months ago carries eighteen months of unpatched vulnerabilities. Rebuilding images on a schedule is the equivalent of updating packages, and it is the step people skip.
Containers move where the work is, not whether it exists.
Write down what you installed and why
Six months on, a server accumulates packages nobody can account for. Nobody removes them, because nobody is certain what would break.
A short file listing what you installed, why, and what depends on it is worth more than it sounds: it is what makes the server rebuildable, and it is what lets you remove things safely.
Keep it off this server, alongside your other rebuild notes.
Test the restart
After installing anything that runs as a service, confirm it is enabled at boot and then reboot deliberately.
A service that works until the first reboot is a service that will be missing after an unplanned one, and that is when nobody is in a position to notice which of several things failed to come back.
Remove what you stop using
Uninstall rather than disable. A disabled service still has its code on disk and still needs patching, and it is one more thing in the way when you are working out what a server actually runs.
Whatever is installed, it needs to come back on its own after a reboot instead of being started by hand. How to Run an Application as a systemd Service walks through arranging that.
Know where a package put things
Installing from the distribution is the right default and it leaves files in several places that are not obvious afterwards.
rpm -ql nginx 2>/dev/null | head -20 dpkg -L nginx 2>/dev/null | head -20 systemctl cat nginx --no-pager | head -15
Reading the file list answers where the configuration lives, where the logs go and what the service definition actually runs, which is faster than searching for them.
The service definition matters most, since it names the user the application runs as and any limits applied to it. Both are frequently different from what the documentation assumes.
Keep your changes where an update will not remove them
Editing a file the package manager owns means the next update either overwrites your change or refuses to proceed.
systemctl edit nginx ls -la /etc/systemd/system/nginx.service.d/ 2>/dev/null find /etc -name '*.rpmnew' -o -name '*.dpkg-dist' 2>/dev/null | head
Override directories exist precisely for this, and a change placed there survives updates and is visible as an addition rather than hidden inside a large file.
The last command finds configuration files an update could not replace because you had edited them. Each is a change waiting to be reconciled, and they accumulate silently until something behaves unexpectedly.
Decide what happens when the application fails
An application installed and started is not the same as one that recovers, and the difference is a few lines in the service definition.
systemctl show nginx -p Restart -p RestartSec 2>/dev/null systemctl is-enabled nginx 2>/dev/null
Without a restart policy the process stays down until somebody notices. With one set too aggressively, a failing application restarts continuously and fills the log rather than reporting the problem.
Enable it at boot and reboot deliberately once to confirm. A machine that has been running for a year has never demonstrated that its applications come back.
Keep a list of what is installed and why
A server accumulates software over years and the reason for each addition is lost within months. Record what was installed, on what date, for which application, and what would make it removable. Without that, nobody can safely remove anything, and the machine carries every experiment anybody ever ran. The list also answers the question that arises during an incident, which is whether a particular component is something you depend on or something left over from a trial. Three lines per item is enough to be useful.