Administering a server is mostly four questions asked in a loop: what is running, what is it doing to the machine, what did it write in the log, and who can reach it. Around forty commands answer those questions, and once they are in your fingers you will go months without needing a forty-first. This post is those commands, with the flags that make them useful and the traps that make them expensive. It assumes Debian or Ubuntu, because that is what most VDS images are, but everything here except apt works the same on any modern distribution.
Two habits are worth building before any of it. First, man ls and ls --help are faster than a search engine and they describe the version you actually have, which is not always the version in the tutorial you found. Second, press Ctrl-r and start typing to search backwards through your shell history. Most of what you need to run today, you ran last week.
The daily ten#
These are the ones you type without thinking about them. Learn the flags in the right-hand column and you have covered most of a working day.
| Command | Worth knowing |
|---|---|
ls | ls -lah for long, hidden and human-readable sizes |
cd | cd - returns to the previous directory |
pwd | Prints where you are, useful inside scripts |
cat | Only for short files; use less for anything longer than a screen |
less | / searches, G jumps to the end, q quits, -S stops wrapping |
cp | cp -a preserves permissions, ownership and timestamps |
mv | Rename and move are the same operation |
rm | rm -rf has no undo and no recycle bin |
mkdir | mkdir -p a/b/c creates the whole path |
nano | The editor that is already installed and does not need a tutorial |
A few small ones sit alongside them. stat file prints permissions, ownership, size and all three timestamps, which settles arguments about whether a config was really edited. file something tells you what a file actually is rather than what its extension claims, which is how you discover that the "zip" somebody uploaded is a RAR archive. nproc prints the number of CPUs the machine believes it has, and you need that number to interpret load averages later in this post.
Reading files and logs#
Most of the work is reading. The trick is not reading everything.
$ tail -n 200 /var/log/nginx/error.log # last 200 lines$ tail -F /var/log/nginx/error.log # follow, survives rotation$ head -n 40 /etc/nginx/nginx.conf # first 40 lines$ less +G /var/log/syslog # open at the end$ wc -l /var/log/auth.log # how many lines is thistail -f follows a file by its open handle. When log rotation renames the file underneath you, -f keeps watching a file nobody writes to any more and you sit there believing the server has gone quiet. tail -F reopens by name and survives the rotation. Use the capital.
For anything managed by systemd, which on a modern box is nearly everything, the log is not a file at all:
$ journalctl -u nginx -f # follow one unit$ journalctl -u minecraft --since "30 min ago"$ journalctl -b -p err # errors since this boot$ journalctl -k # kernel messages only$ journalctl --disk-usage$ journalctl --vacuum-size=200M # when the journal has grown-u selects a unit, -b limits to the current boot, -p err filters by priority, and --since takes plain English like "yesterday" or "2026-09-20 18:00". The combination that solves most incidents is journalctl -u <name> -b --no-pager | less, then search inside less. If the service is a game server, the interesting lines are usually the last thirty before it stopped, not the stack trace at the end. Logs worth keeping covers which of these are worth shipping somewhere permanent.
Compressed archives are searchable without unpacking them. zcat, zless and zgrep behave like their plain versions on .gz files, so zgrep "Invalid user" /var/log/auth.log.*.gz works across a fortnight of rotated logs in one go.
Finding things: find, grep and which#
grep searches inside files. find searches for files. People reach for the wrong one constantly.
$ grep -rn "listen" /etc/nginx/ # recursive, with line numbers$ grep -i "error" app.log | tail -n 50 # case-insensitive$ grep -c "404" access.log # count instead of printing$ grep -v "healthcheck" access.log # everything except$ grep -B2 -A5 "Exception" app.log # with context around each hit-rn is the pair to memorise: recursive and with line numbers, so you get file:line: text and can open the file straight at the right place. -B and -A print lines before and after the match, which is the difference between knowing an exception happened and knowing what request caused it.
$ find /home -type f -name "*.db" -size +50M$ find /var/log -type f -name "*.gz" -mtime +14 -delete$ find . -type d -name node_modules -prune -o -type f -name "*.js" -print$ which python3 # which binary will run$ command -v node # the portable version of the same questionfind reads as a sentence: where to look, what type, what name, and then what to do about it. -mtime +14 means modified more than fourteen days ago. -delete is final, so run the command once without it and read the list first. That two-step is not caution theatre; find with a slightly wrong -name pattern will match more than you expected roughly once per year.
which answers "what will actually run when I type this", which matters when you have installed a second Node or Python and the old one is still first in PATH. echo $PATH when the answer surprises you.
Is the box busy: processes, memory and CPU#
Four commands tell you whether the machine is in trouble, and in what way.
$ uptime 14:22:01 up 12 days, 3:41, 1 user, load average: 1.82, 1.44, 0.98$ free -h$ ps aux --sort=-%mem | head -n 12$ top # or htop, if you installed itThe load average is three numbers: the last one, five and fifteen minutes. Compare them against nproc. On a two-vCPU box, a load of 2.00 means fully busy, 4.00 means work is queuing, and 0.8 means fine. The direction matters as much as the value: 0.5 1.4 2.1 is a machine recovering, and 2.1 1.4 0.5 is a machine that is about to have a bad afternoon. Load counts processes waiting on disk as well as on CPU, so a high load with idle CPUs usually means storage, not compute.
free -h prints a row you should mostly ignore and a column you should not. The free column is almost always small, because Linux uses spare memory as disk cache and gives it back on demand. The number that tells you whether you have memory is available. If available is under a few hundred megabytes, you are one bad allocation away from the kernel killing something, which is a topic of its own in swap and the OOM killer.
ps aux --sort=-%mem | head is the fastest answer to "what is eating the RAM". Swap %mem for %cpu to ask the other question. top is the live version; htop is the same thing with colours, scrolling and a tree view, and is worth the thirty seconds it takes to install. Inside top, press M to sort by memory, P for CPU, and 1 to expand the per-core display.
When something has to stop:
$ kill 4821 # polite: SIGTERM, lets it clean up and save$ kill -9 4821 # SIGKILL: immediate, no save, last resort$ pkill -f "valheim_server"$ pgrep -af java # find the PID and the full command line firstThe distinction is not academic for game servers. A SIGTERM gives most of them the chance to write the world to disk; a SIGKILL throws away everything since the last autosave. Reach for -9 only after a polite stop has failed for a minute.
Disk space and what filled it#
A full disk breaks things in unhelpful ways: the database refuses writes, the log stops, and the service dies with an error about something unrelated. Check space before you debug anything strange.
$ df -h # space per filesystem$ df -i # inodes, when df -h says there is room$ du -sh /var/* 2>/dev/null | sort -h$ du -sh * | sort -h # then descend into the biggest one$ ncdu /var # interactive, if you install it$ lsof +L1 # deleted files still held opendu -sh * | sort -h and then cd into the largest result is the whole technique. Three or four repetitions land you on the directory that matters. sort -h sorts human-readable sizes properly, so 2G ranks above 900M rather than below it alphabetically.
Two failure modes look like magic until you have seen them. The first is inode exhaustion: df -h shows 40% used, writes still fail, and df -i shows 100% of inodes gone, usually to a session directory or a cache holding millions of tiny files. The second is a deleted file that a process still has open. The space is not returned until the file handle closes, so du and df disagree with each other. lsof +L1 lists exactly those files; restarting the process that holds them frees the space instantly. This is almost always a log file somebody deleted instead of truncating. The right way to empty a log that is in use is truncate -s 0 /path/to/file, not rm.
Services and systemd#
Everything that should survive a reboot belongs to systemd. Five subcommands cover the job.
$ systemctl status nginx$ systemctl restart nginx$ systemctl enable --now fail2ban # start it and start it at boot$ systemctl disable --now something # stop it and stop it at boot$ systemctl daemon-reload # after editing any unit file$ systemctl list-units --type=service --state=running$ systemctl list-timers$ systemd-analyze blame # what made this boot slowenable and start are separate ideas and mixing them up is the single most common systemd mistake. start runs it now. enable makes it run at boot. A service you started but never enabled disappears the first time the box reboots, usually months later, and nobody connects the two events. --now does both.
systemctl status prints the last ten log lines under the state, which is often enough to see why a start failed. When it is not, journalctl -u <name> -n 100 --no-pager gives you the rest. After editing a unit file you must run daemon-reload before restart, or systemd keeps using the version it parsed at boot and you debug a change that was never applied. Writing the unit files themselves is a longer subject, covered in systemd services for your apps.
Users, permissions and ownership#
Permission errors account for a large share of "it works on my machine". The fix is nearly always ownership, not mode.
$ id # who am I, and in what groups$ adduser deploy # interactive, creates the home dir$ usermod -aG sudo deploy # note the -a; without it you replace groups$ sudo -u minecraft -i # become a service user for one session$ chown -R minecraft:minecraft /srv/minecraft$ chmod 700 ~/.ssh$ chmod 600 ~/.ssh/authorized_keys$ chmod +x start.shThe -a in usermod -aG is append. Leaving it out replaces every group the user is in with the one you named, which is how people remove their own sudo access. Type it slowly.
Numeric modes are three digits for owner, group and everyone else, where read is 4, write is 2 and execute is 1. So 644 is owner read and write, everyone else read; 755 adds execute for directories and scripts; 600 is owner only. SSH refuses to use a private key or an authorized_keys file that is readable by anyone else, silently falling back to a password prompt, and that is the single most common reason a key "does not work" - see SSH keys and hardening for the rest of that story.
Run services as their own unprivileged user, one per service, with the files owned by that user. It costs a minute at setup and it is the difference between one compromised game server and a compromised machine. When you need to act as that user, sudo -u minecraft -i gives you a login shell as them rather than making you log out.
Networking: what is listening, what can reach it#
netstat is deprecated and missing from minimal images. ss replaced it and is faster.
$ ss -tulpen # every TCP and UDP listener, with the process$ ss -tn state established # current connections$ ip -brief addr # the machine's addresses, one line each$ ip route # where traffic goes by default$ ufw status numbered # what the firewall allowsss -tulpen is the most valuable single command in this post. Run it on the day you finish setting up a server and again once a month, and keep the output short. Every line is a door. A database bound to 0.0.0.0 instead of 127.0.0.1 is the second most common way small servers are compromised, and this command is how you find it in five seconds. What to do about the doors you find belongs to the ufw firewall guide and firewall rules that matter.
For testing from outside the box:
$ curl -I https://example.com # response headers only$ curl -sS -o /dev/null -w "%{http_code} %{time_total}s\n" https://example.com$ dig +short A example.com @1.1.1.1 # ask a specific resolver$ ping -c 4 203.0.113.10$ mtr -rwc 50 203.0.113.10 # 50 packets, report mode$ nc -zv 203.0.113.10 25565 # is this TCP port openRun dig against a public resolver with @1.1.1.1 when you have just changed a record, because your own resolver is probably still holding the old answer until its TTL expires. nc -zv tests TCP; it cannot meaningfully test a UDP game port, because there is nothing to hand back a refusal. Reading an mtr report properly is its own skill, and latency, jitter and packet loss explains which column actually matters.
Transfers, archives and staying attached#
Getting files on and off the machine, and keeping things running after you close the terminal.
$ scp world.zip deploy@203.0.113.10:/srv/ # one file, quick$ rsync -avh --progress /srv/world/ backup:/srv/world/$ rsync -avh --delete src/ dest/ # make dest match src exactly$ tar -czf world-2026-09-21.tar.gz worlds_local/$ tar -xzf archive.tar.gz -C /srv/target/$ unzip plugins.zip -d plugins/rsync beats scp for anything repeated, because it copies only what changed and can resume. The trailing slash rule is the part that catches everybody: rsync -a src/ dest/ copies the contents of src into dest, while rsync -a src dest/ creates dest/src. Test with --dry-run the first time, especially with --delete, which will remove anything on the destination that is not on the source.
$ tmux new -s valheim # new named session Ctrl-b then d # detach, leaving it running$ tmux attach -t valheim # come back later$ tmux ls # what sessions exist$ watch -n 5 'ss -tn state established | wc -l'tmux keeps a process alive after your SSH session drops, and lets you reattach to the same terminal from anywhere. It is the right tool for a long compile or a migration you are babysitting. It is the wrong tool for running a game server permanently: a process in tmux does not start at boot, does not restart when it crashes, and is invisible to any monitoring you set up later. Use a systemd unit for anything that is meant to be always on, and keep tmux for work you are watching.
Finally, keeping the system itself current:
$ apt update && apt full-upgrade -y$ apt list --upgradable$ apt autoremove --purge$ needrestart # which services are running old librariesapt update refreshes the package lists and upgrades nothing, which trips up people arriving from other package managers. full-upgrade is upgrade plus the right to remove a package when an upgrade requires it, which is what you want on a server you actually intend to keep patched. After a library upgrade, the running processes still hold the old copy in memory until they restart; needrestart tells you which ones. The whole first-day routine, in order, is in the first hour on a new VDS.
On a managed RE:NODE plan none of this applies, because you do not get a shell on the host. What you get instead is an unfiltered console with command history and tab completion, which is the game or app process's own input and output rather than a Linux prompt, plus an in-browser file manager and per-server SFTP credentials. That covers configuration, uploads and log reading without any of the commands above. A VDS is the other trade: full root access, and every command on this page becomes your responsibility. Choosing between a VDS and a game panel puts numbers on which one is cheaper for your case.
FAQ#
What is the difference between apt and apt-get?
apt is the newer front end intended for people typing at a terminal, with a progress bar and slightly friendlier output. apt-get has a stable interface intended for scripts. For interactive use they do the same things; use apt and do not think about it again.
Why does free -h say I have almost no free memory?
Because Linux uses otherwise idle memory as a disk cache, and that is a good thing. Cached memory is handed back the moment a program asks for it. Read the available column, not free, and only worry when available gets small.
How do I keep a program running after I log out?
For anything permanent, a systemd unit with Restart=on-failure. For a one-off task you are supervising, tmux or nohup command &. The difference is that systemd restarts the program after a crash and starts it again after a reboot, and tmux does neither.
How do I find which process is using a port?
ss -tulpen lists every listener with the process name and PID in the last column. If the port is in use but nothing appears, you are probably not root - run it with sudo, because the process column is hidden for other users' processes.
Do I need to learn vim?
No. nano is installed on nearly every distribution, shows its shortcuts at the bottom of the screen, and edits a config file perfectly well. Learn enough vim to quit it - Esc then :q! - because it is sometimes the default editor, and leave the rest until you want it.
What should I check first when a server feels slow?
In this order: uptime for load against nproc, free -h for the available column, df -h for a full disk, then ps aux --sort=-%cpu | head. Those four take twenty seconds together and identify the cause most of the time.




კომენტარები
სრულიად ანონიმურად: ანგარიშის, ელფოსტის და cookie-ის გარეშე. ინახება მხოლოდ სახელი, ტექსტი და დრო - სხვა არაფერი. ბმულების რაოდენობა ლიმიტირებულია.