Jhonatan Pinheiro
Loading page...
Jhonatan Pinheiro
Loading page...
Jhonatan Pinheiro
Loading page...
Pipes and redirection, sed and awk, shell scripts, systemd, cron, SSH, networking, disks and logs: 200 items with practical examples.
You already navigate, edit and install packages. These 200 items are the turning point: combining commands, automating with scripts, administering services with systemd, scheduling tasks, mastering SSH and diagnosing disk, network and logs.
It is the set that separates someone who uses the terminal from someone who administers the server.
The Unix philosophy in one sentence: each command does one thing well, and the pipe (
|) joins it all together. Most of this post is about that.
What turns isolated commands into a single tool.
Sends the output of one command into the input of the next.
ps aux | grep nginx | wc -l> overwrites the file; >> appends to the end.
comando > saida.txt
comando >> log.txt1 is standard output, 2 is the error.
comando > saida.txt 2> erros.txtThe order matters: redirect the file first, then join 2 to 1.
comando > tudo.txt 2>&1
comando &> tudo.txt # a bash shortcut/dev/null is the system's drain.
comando 2>/dev/null
comando >/dev/null 2>&1< feeds the command with the file's content.
sort < nomes.txt
while read linha; do echo "$linha"; done < lista.txtA block of text straight into the command. Heavily used for configuration and SQL.
cat <<EOF > config.yml
servidor: producao
porta: 8080
EOFSends a string straight into the input.
grep "erro" <<< "$conteudo"Saves to a file and keeps the pipe going. Essential with sudo.
comando | tee saida.txt
echo "linha" | sudo tee -a /etc/hostsSome commands do not read from the pipe; xargs solves that.
find . -name "*.log" | xargs rm
cat urls.txt | xargs -n1 curl -sI-P processes several at once. A huge gain on a repetitive task.
cat urls.txt | xargs -P 8 -n 1 curl -sO$(...) inserts the output of one command inside another.
echo "Hoje é $(date +%F)"
kill $(pgrep -f meu-script)<(...) turns a command's output into a temporary file.
diff <(ls pasta1) <(ls pasta2)
comm -13 <(sort a.txt) <(sort b.txt)Generates lists and sequences without a loop.
mkdir -p projeto/{src,test,docs}
touch arquivo{1..5}.txt
cp config.yml{,.bak}Does the maths in the shell itself.
echo $((2 + 3 * 4))
total=$((total + 1))Double quotes expand variables; single quotes expand nothing.
nome="Jhonatan"
echo "Olá $nome" # Olá Jhonatan
echo 'Olá $nome' # Olá $nomeWithout quotes, a space in the value becomes two arguments — the source of half the bugs in scripts.
arquivo="meu arquivo.txt"
rm "$arquivo" # right
rm $arquivo # tries to delete "meu" and "arquivo.txt"$? holds the result of the last command: 0 is success.
comando
if [ $? -ne 0 ]; then echo "falhou"; fiWithout it, a pipe only fails if the last command fails.
set -o pipefail
falso | true; echo $? # now it reports the failureRuns according to the success of the previous command.
make build && make deploy
comando || echo "falhou" >&2The trio that processes text on any server, with nothing to install.
Patterns instead of fixed text.
grep -E "^[0-9]{3}-[0-9]{4}$" telefones.txtExtracts the fragment, not the whole line.
grep -oE "[0-9]{1,3}(\.[0-9]{1,3}){3}" access.log | sort | uniq -c | sort -nrEnables lookahead, \d, \s and the like.
grep -P "(?<=usuario=)\w+" app.logSearches inside a rotated log without decompressing it.
zgrep "erro" /var/log/syslog.2.gz
grep -a "texto" arquivo.binAvoids sweeping node_modules and .git.
grep -r "TODO" . --exclude-dir={node_modules,.git,dist}The most used operation: swapping text.
sed 's/antigo/novo/' arquivo.txt # the first occurrence in the line
sed 's/antigo/novo/g' arquivo.txt # all of them-i writes to the file itself. Always make a backup with -i.bak.
sed -i.bak 's/porta=8080/porta=9090/' config.iniAvoids escaping slashes when working with paths.
sed 's|/var/www|/srv/www|g' config.confRestricts the substitution to a line or a range.
sed '3s/erro/ok/' arquivo.txt
sed '10,20s/dev/prod/g' arquivo.txtRemoves by number, by pattern or empty ones.
sed '5d' arquivo.txt
sed '/^#/d' config.conf # comments
sed '/^$/d' arquivo.txt # blank linesBefore (i), after (a) or in place (c).
sed '1i #!/bin/bash' script.sh
sed '/\[servidor\]/a porta=8080' config.ini-n silences the default output; p prints what matters.
sed -n '10,20p' arquivo.txt
sed -n '/INICIO/,/FIM/p' log.txtCaptures parts and reorders them.
echo "2026-08-10" | sed -E 's/([0-9]{4})-([0-9]{2})-([0-9]{2})/\3\/\2\/\1/'Batch substitution, combined with find.
find . -name "*.md" -exec sed -i 's/http:/https:/g' {} +Splits the line into fields automatically.
awk '{print $1, $3}' arquivo.txt
ps aux | awk '{print $2, $11}'-F sets the field separator.
awk -F: '{print $1}' /etc/passwd
awk -F, '{print $2}' dados.csvIt only processes the lines matching the condition.
awk '$3 > 100 {print $1, $3}' vendas.txt
awk '/erro/ {print}' app.logAccumulates values and prints at the end.
awk '{soma += $2} END {print "Total:", soma}' vendas.txtA counter and a sum in the same pass.
awk '{soma += $1; n++} END {print "Média:", soma/n}' numeros.txtAn associative array: the terminal's "GROUP BY".
awk '{contagem[$1]++} END {for (chave in contagem) print chave, contagem[chave]}' acessos.logNR is the line number; NF, the number of fields.
awk 'NR > 1 {print $1}' dados.csv # skips the header
awk '{print NF}' arquivo.txt # fields per lineprintf gives full control of the format.
awk '{printf "%-20s %8.2f\n", $1, $2}' precos.txtCombines filters as in SQL.
awk -F, '$3 == "SP" && $4 > 1000 {print $1}' clientes.csvOFS defines how the fields come out.
awk -F: 'BEGIN {OFS=","} {print $1, $3}' /etc/passwdThe classic recipe: the top 10 IPs by number of hits.
awk '{print $1}' access.log | sort | uniq -c | sort -nr | head -10awk combined with a status filter.
awk '$9 == 500 {print $7}' access.log | sort | uniq -c | sort -nr | headExtracts the metric straight from the log.
awk '{soma += $NF; n++} END {printf "média: %.3fs\n", soma/n}' tempos.logcut is faster for fixed columns; awk copes with variable spacing.
cut -d, -f1,3 dados.csv # simple and fast
awk '{print $1, $3}' saida.txt # irregular spacingJoins files by column or by key, like a JOIN.
paste nomes.txt idades.txt
join -t, -1 1 -2 1 clientes.csv pedidos.csvMakes a messy output readable.
mount | column -t
cat /etc/passwd | column -t -s:Automating what you repeat — with the fewest possible traps.
The first line: it says which interpreter runs the file.
#!/usr/bin/env bashWithout execute permission, the script does not run.
chmod +x script.sh
./script.shThe three lines that prevent most bugs: exit on error, complain about undefined variables and propagate a pipe failure.
set -euo pipefailNo space around the =. Quotes when using them.
nome="Jhonatan"
echo "Olá, $nome"Without local, everything is global — and a repeated name becomes a silent bug.
minha_funcao() {
local contador=0
contador=$((contador + 1))
}$1, $2... and $@ for all of them.
echo "primeiro: $1"
echo "todos: $@"
echo "quantidade: $#"Uses the default when the variable is empty.
porta="${PORTA:-8080}"
destino="${1:?informe o destino}" # aborts with a messageA conditional with double brackets (a bash feature, safer).
if [[ -f config.yml ]]; then
echo "config encontrado"
fiThe full chain.
if [[ $1 == "prod" ]]; then
echo "produção"
elif [[ $1 == "hml" ]]; then
echo "homologação"
else
echo "ambiente desconhecido" >&2
exit 1
fiExists, is a directory, is readable, has content.
[[ -e caminho ]] # exists
[[ -f arquivo ]] # is a file
[[ -d pasta ]] # is a directory
[[ -r arquivo ]] # readable
[[ -s arquivo ]] # is not emptyEmpty, equal, different, matches a pattern.
[[ -z "$var" ]] # empty
[[ -n "$var" ]] # not empty
[[ "$a" == "$b" ]]
[[ "$arquivo" == *.log ]]Comparing numbers uses its own operators.
[[ $n -eq 10 ]] # equal
[[ $n -ne 10 ]] # different
[[ $n -gt 10 ]] # greater
[[ $n -lt 10 ]] # lessMore readable than a stack of elifs.
case "$1" in
start) iniciar ;;
stop) parar ;;
restart) parar; iniciar ;;
*) echo "uso: $0 {start|stop|restart}"; exit 1 ;;
esacIterates over literal values.
for ambiente in dev hml prod; do
echo "implantando em $ambiente"
doneUse the glob directly — never the output of ls.
for arquivo in *.log; do
gzip "$arquivo"
doneA range of numbers with a sequence or in C style.
for i in {1..10}; do echo "$i"; done
for ((i = 0; i < 10; i++)); do echo "$i"; doneRepeats while the condition is true.
contador=0
while [[ $contador -lt 5 ]]; do
echo "$contador"
contador=$((contador + 1))
doneThe correct way to process a file: IFS= and -r preserve spaces and backslashes.
while IFS= read -r linha; do
echo "Linha: $linha"
done < arquivo.txtThe inverse of while: it repeats until the condition becomes true.
until curl -sf http://localhost:8080/health; do
echo "aguardando o serviço..."
sleep 2
doneLeaves the loop or skips to the next iteration.
for i in {1..10}; do
[[ $i -eq 5 ]] && continue
[[ $i -eq 8 ]] && break
echo "$i"
doneThey group logic and receive arguments like the script does.
saudacao() {
local nome="$1"
echo "Olá, $nome"
}
saudacao "Jhonatan"return gives back an exit code; to return text, use echo and capture it.
soma() { echo $(( $1 + $2 )); }
resultado=$(soma 2 3)An indexed list, with all the elements in [@].
servidores=("web1" "web2" "db1")
echo "${servidores[0]}"
echo "${#servidores[@]}" # count
for s in "${servidores[@]}"; do echo "$s"; doneA key-value map in bash 4+.
declare -A porta
porta[web]=80
porta[api]=3000
for servico in "${!porta[@]}"; do echo "$servico: ${porta[$servico]}"; doneLength, slice and prefix/suffix removal, without calling an external command.
s="arquivo.tar.gz"
echo "${#s}" # length
echo "${s%.gz}" # removes the suffix
echo "${s#*.}" # removes up to the first dot
echo "${s/tar/zip}" # substitutesEnsures the temporary file is deleted even if the script dies.
temporario=$(mktemp)
trap 'rm -f "$temporario"' EXIT
# ... use the filemktemp creates one with a unique name and safe permissions.
arquivo=$(mktemp)
pasta=$(mktemp -d)-p shows the prompt; -s hides what is typed.
read -p "Nome: " nome
read -s -p "Senha: " senha; echoThe minimum protection in a destructive script.
read -p "Apagar tudo? (s/N) " resposta
[[ "$resposta" == "s" ]] || exit 0Flag parsing in the Unix style.
while getopts "a:p:v" opcao; do
case $opcao in
a) ambiente="$OPTARG" ;;
p) porta="$OPTARG" ;;
v) verboso=1 ;;
*) exit 1 ;;
esac
doneA simple log function that saves debugging later.
log() { echo "[$(date +'%F %T')] $*"; }
log "iniciando implantação"An error message should not pollute the script's output.
echo "erro: arquivo não encontrado" >&2flock prevents two simultaneous instances — essential in cron.
flock -n /tmp/meu-script.lock ./meu-script.sh || echo "já está rodando"-x prints each command as it runs.
bash -x script.sh
set -x # turns it on mid-script
set +x # turns it offIt points out the classic mistakes before you suffer from them. Always use it.
shellcheck script.shHow a service starts at boot, restarts on its own and records what it did.
Current state, PID and the last lines of the log.
systemctl status nginxThe everyday trio.
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginxRereads the configuration while keeping open connections.
sudo systemctl reload nginx
sudo systemctl reload-or-restart nginxenable only configures it; --now also starts it right away.
sudo systemctl enable nginx
sudo systemctl enable --now nginxStops it coming up at the next boot.
sudo systemctl disable --now nginxIt returns an exit code — perfect for a script.
systemctl is-active nginx
systemctl is-enabled nginxEverything that exists, or only what is running.
systemctl list-units --type=service
systemctl list-units --type=service --state=runningThe first query when you log into a server with a problem.
systemctl --failedA minimal unit file to run your application.
# /etc/systemd/system/minha-api.service
[Unit]
Description=Minha API
After=network.target
[Service]
Type=simple
User=app
WorkingDirectory=/opt/minha-api
ExecStart=/usr/bin/node server.js
Restart=on-failure
RestartSec=5
Environment=NODE_ENV=production
[Install]
WantedBy=multi-user.targetMandatory after creating or editing any service file.
sudo systemctl daemon-reloadCreates an override instead of altering the package's file.
sudo systemctl edit nginx
sudo systemctl cat nginx # shows the effective unitA separate file, outside git, with the credentials.
# in the unit:
EnvironmentFile=/etc/minha-api/env
# in the file:
DATABASE_URL=postgres://...After orders them; Requires forces the other to be active.
[Unit]
After=postgresql.service
Requires=postgresql.servicesystemd applies cgroups without you having to configure them by hand.
[Service]
MemoryMax=512M
CPUQuota=50%Options that greatly reduce the damage in case of a breach.
[Service]
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/lib/minha-apiStops it coming up at all, not even through a dependency.
sudo systemctl mask apache2
sudo systemctl unmask apache2Graphical mode, multi-user or rescue.
systemctl get-default
sudo systemctl set-default multi-user.target
sudo systemctl isolate rescue.targetShows who is holding up the startup.
systemd-analyze
systemd-analyze blame | head -20Understands why one service starts before another.
systemctl list-dependencies nginxThe modern alternative to cron, with integrated logging and dependency control.
# /etc/systemd/system/backup.timer
[Unit]
Description=Backup diário
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target
# sudo systemctl enable --now backup.timer
# systemctl list-timersTasks that run on their own — and the details that make them fail silently.
Opens your user's schedule.
crontab -e
sudo crontab -e -u www-data # another user'sSee what is scheduled; -r deletes everything (careful).
crontab -l
crontab -rMinute, hour, day of month, month, day of week.
# * * * * * comando
# │ │ │ │ └── day of week (0-7, 0 and 7 = Sunday)
# │ │ │ └──── month (1-12)
# │ │ └────── day of month (1-31)
# │ └──────── hour (0-23)
# └────────── minute (0-59)The schedules that cover nearly everything.
0 3 * * * /opt/backup.sh # every day at 3 a.m.
*/15 * * * * /opt/checa-saude.sh # every 15 minutes
0 9 * * 1 /opt/relatorio.sh # Mondays at 9 a.m.
0 0 1 * * /opt/fechamento.sh # the first day of the monthMore readable than the five fields.
@daily /opt/backup.sh
@hourly /opt/sync.sh
@reboot /opt/inicia.shCron runs with a minimal environment. Always use the absolute path.
# Silent failure:
0 3 * * * backup.sh
# Right:
0 3 * * * /usr/local/bin/backup.shWithout redirection, the output goes to local email — which nobody reads.
0 3 * * * /opt/backup.sh >> /var/log/backup.log 2>&1Declared at the top of the file, they apply to every line.
SHELL=/bin/bash
PATH=/usr/local/bin:/usr/bin:/bin
MAILTO=""Just drop an executable script into these folders.
ls /etc/cron.daily/
ls /etc/cron.d/
cat /etc/crontabA slow task can start again before it finishes. flock solves it.
*/5 * * * * flock -n /tmp/sync.lock /opt/sync.shRuns a single time at a future moment.
echo "/opt/manutencao.sh" | at 02:00
atq # lists
atrm 3 # removesRun it with the same environment as cron to catch PATH problems.
env -i /bin/bash --noprofile --norc -c "/opt/backup.sh"Remote access — and how to use it without a password and without risk.
User, host and port.
ssh usuario@servidor
ssh -p 2222 usuario@servidorEd25519 is the current standard: smaller and safer than RSA.
ssh-keygen -t ed25519 -C "voce@email.com"Enables login without a password.
ssh-copy-id usuario@servidor
ssh-copy-id -i ~/.ssh/id_ed25519.pub usuario@servidorSSH refuses the key if the permissions are loose.
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
chmod 600 ~/.ssh/authorized_keysAliases that save you memorising IP, port and user.
# ~/.ssh/config
Host prod
HostName 203.0.113.10
User deploy
Port 2222
IdentityFile ~/.ssh/id_ed25519
# now: ssh prodKeeps the unlocked key for the session.
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519
ssh-add -lRuns and comes back, without opening an interactive session.
ssh prod "df -h && uptime"Brings a remote port to your machine — the safe way to reach a database.
ssh -L 5432:localhost:5432 prod
# now localhost:5432 is the server's PostgresExposes a port of yours on the remote server.
ssh -R 8080:localhost:3000 prodBrowse as if you were on the server's network.
ssh -D 1080 prodReaches the internal machine by way of the edge host.
ssh -J bastion usuario@servidor-internoPrevents the drop from inactivity.
# ~/.ssh/config
Host *
ServerAliveInterval 60
ServerAliveCountMax 3Multiplexing: the second connection to the same host is instantaneous.
Host *
ControlMaster auto
ControlPath ~/.ssh/cm-%r@%h:%p
ControlPersist 10mThe three changes that cut the attack surface the most.
# /etc/ssh/sshd_config
PermitRootLogin no
PasswordAuthentication no
Port 2222
sudo systemctl reload sshdValidate before reloading — a mistake here locks you out.
sudo sshd -t-v shows each step of the authentication.
ssh -vvv usuario@servidorHas the server's key changed? Remove the old entry (and confirm it is not an attack).
ssh-keygen -R servidorscp and rsync use the same configuration from ~/.ssh/config.
scp arquivo.txt prod:/tmp/
rsync -avz ./site/ prod:/var/www/site/Diagnosing connectivity, ports and traffic for real.
It lasts until the next reboot — useful during maintenance.
sudo ip addr add 192.168.1.50/24 dev eth0
sudo ip link set eth0 upAdds or changes the gateway.
sudo ip route add default via 192.168.1.1
ip route get 8.8.8.8 # which way the traffic leavesModern persistent configuration.
# /etc/netplan/01-config.yaml
network:
version: 2
ethernets:
eth0:
addresses: [192.168.1.50/24]
routes:
- to: default
via: 192.168.1.1
nameservers:
addresses: [1.1.1.1, 8.8.8.8]
# sudo netplan applyCommon on RHEL and desktops.
nmcli device status
nmcli connection show
sudo nmcli connection modify eth0 ipv4.addresses 192.168.1.50/24Who is connected to what, right now.
ss -tan state established
ss -tp # with the processDetects a client abusing the service or a simple attack.
ss -tan | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -nr | headSees the actual packet. Irreplaceable when the log does not explain.
sudo tcpdump -i eth0 port 80 -n
sudo tcpdump -i any host 192.168.1.10 -w captura.pcap-A shows the ASCII content — good for HTTP.
sudo tcpdump -i eth0 -A -s0 'tcp port 80 and host api.exemplo.com'Finds out what is open. Only scan what is yours.
nmap -sT localhost
nmap -p 1-1000 192.168.1.10
nmap -sV 192.168.1.10 # detects the service and versionTests a port, transfers a file and stands up an improvised server.
nc -zv servidor 443
nc -l 9000 # listens on the port
nc servidor 9000 < arquivo.txtShows where the time goes: DNS, connection, TLS, response.
curl -w "dns:%{time_namelookup} conn:%{time_connect} tls:%{time_appconnect} total:%{time_total}\n" -o /dev/null -s https://exemplo.comValidity, chain and issuer.
openssl s_client -connect exemplo.com:443 -servername exemplo.com < /dev/null 2>/dev/null | openssl x509 -noout -dates -subject -issuerA simple layer over iptables, common on Ubuntu.
sudo ufw status verbose
sudo ufw allow 22/tcp
sudo ufw allow from 192.168.1.0/24 to any port 5432
sudo ufw enableThe default on RHEL and Fedora.
sudo firewall-cmd --list-all
sudo firewall-cmd --add-service=https --permanent
sudo firewall-cmd --reloadWhat is really applied in the kernel.
sudo iptables -L -n -v
sudo nft list ruleset # nftables, the successorOn systems with systemd-resolved, /etc/resolv.conf is misleading.
resolvectl status
cat /etc/resolv.confAfter changing a record, the local cache gets in the way of the test.
sudo resolvectl flush-cachesiperf3 measures the link's real throughput.
# on the server
iperf3 -s
# on the client
iperf3 -c 192.168.1.10 -t 30Finds out who is consuming the network.
sudo nethogs
sudo iftop -i eth0The physical address, and powering a machine on remotely.
ip link show eth0 | grep ether
wakeonlan 00:11:22:33:44:55From the raw disk to the mount point that survives a reboot.
A view of devices, partitions and mount points.
lsblk -f
sudo fdisk -lInteractive, for MBR and simple GPT tables.
sudo fdisk /dev/sdb
# n (new) · p (primary) · w (writes)Better for a disk larger than 2 TB (GPT).
sudo parted /dev/sdb mklabel gpt
sudo parted -a opt /dev/sdb mkpart primary ext4 0% 100%Creates the filesystem on the partition.
sudo mkfs.ext4 /dev/sdb1
sudo mkfs.xfs /dev/sdb1Use the UUID in fstab: the device name can change between boots.
sudo e2label /dev/sdb1 dados
blkid /dev/sdb1An entry in fstab with the UUID and options.
# /etc/fstab
UUID=abcd-1234 /mnt/dados ext4 defaults,noatime 0 2
sudo mount -a # tests without rebootingnoatime reduces writes; ro protects; nosuid/nodev/noexec harden.
sudo mount -o noatime,nodev,nosuid /dev/sdb1 /mnt/dadosOnly with the partition unmounted.
sudo umount /dev/sdb1
sudo fsck -f /dev/sdb1Grows the filesystem after the partition has grown.
sudo resize2fs /dev/sdb1 # ext4
sudo xfs_growfs /mnt/dados # xfs (while mounted)Creates and activates the swap area.
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
swapon --showHow much the kernel prefers to use swap. On a database server, low values.
cat /proc/sys/vm/swappiness
sudo sysctl vm.swappiness=10A logical volume lets you grow the disk without repartitioning.
sudo pvs # physical volumes
sudo vgs # groups
sudo lvs # logical volumesFrom the raw disk to a mountable volume.
sudo pvcreate /dev/sdb
sudo vgcreate dados-vg /dev/sdb
sudo lvcreate -L 50G -n dados-lv dados-vg
sudo mkfs.ext4 /dev/dados-vg/dados-lvThe big advantage: growing with no downtime.
sudo lvextend -L +20G /dev/dados-vg/dados-lv
sudo resize2fs /dev/dados-vg/dados-lvA photo of the volume for a consistent backup.
sudo lvcreate -L 5G -s -n snap /dev/dados-vg/dados-lvAccess the content without burning media.
sudo mount -o loop imagem.iso /mnt/isoNFS and SMB/CIFS.
sudo mount -t nfs servidor:/export /mnt/nfs
sudo mount -t cifs //servidor/compartilhado /mnt/win -o username=userAnticipates a hardware failure.
sudo smartctl -a /dev/sda
sudo smartctl -t short /dev/sdaFinds out who is hammering the disk.
sudo iotop -o
pidstat -d 1Latency and utilisation per device. %util near 100 is a bottleneck.
iostat -xz 1Where the server tells you what happened.
systemd's unified log.
journalctl
journalctl -n 100Filters by unit — the command you will use the most.
journalctl -u nginx
journalctl -u nginx -f # liveCuts out the time window of the incident.
journalctl --since "2026-08-10 14:00" --until "2026-08-10 15:00"
journalctl --since "1 hour ago"Errors and above only.
journalctl -p err
journalctl -p warning..emergThe current boot or the previous one — essential after a crash.
journalctl -b
journalctl -b -1
journalctl --list-bootsKernel messages, with a readable timestamp.
dmesg -T | tail -50
journalctl -kConfirms whether the kernel killed your process for lack of memory.
dmesg -T | grep -i "out of memory"
journalctl -k | grep -i oomJSON format for analysis in an external tool.
journalctl -u nginx -o json > nginx.json
journalctl -u nginx --since today > nginx.txtIt grows up to the configured limit — and fills the disk.
journalctl --disk-usage
sudo journalctl --vacuum-size=500M
sudo journalctl --vacuum-time=30dWhere the traditional services write.
ls -lh /var/log/
sudo tail -f /var/log/auth.log # authentication
sudo tail -f /var/log/syslogRotates, compresses and discards old logs — what stops the disk filling up.
cat /etc/logrotate.conf
sudo logrotate -d /etc/logrotate.d/nginx # simulates
sudo logrotate -f /etc/logrotate.d/nginx # forcesIts own file in /etc/logrotate.d/.
/var/log/minha-api/*.log {
daily
rotate 14
compress
missingok
notifempty
copytruncate
}Makes your script show up in the system log.
logger -t meu-script "backup concluído"
logger -p user.err -t meu-script "falha ao conectar"grep combined with context and a time window.
journalctl -u minha-api --since "30 min ago" | grep -i -C 5 "exception"Failed SSH authentications — the first sign of an attack.
sudo grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -nr | headHardening the basics and automating what repeats.
Always edit with visudo: it validates before saving and stops you locking sudo.
sudo visudo
sudo visudo -f /etc/sudoers.d/deployAllows only what is needed, instead of unrestricted access.
# /etc/sudoers.d/deploy
deploy ALL=(ALL) NOPASSWD: /bin/systemctl restart minha-apiBlocks an IP after failed login attempts.
sudo apt install fail2ban
sudo fail2ban-client status sshd
sudo fail2ban-client set sshd unbanip 192.168.1.10Lets the server apply a critical fix on its own.
sudo apt install unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgradesA simple audit: should what is listening be listening?
sudo ss -tulpn | grep LISTENPost-incident investigation.
sudo find /etc -mtime -2 -type f
sudo find / -mmin -60 -type f -not -path "/proc/*" 2>/dev/nullDetects a modified system binary.
sudo debsums -c # Debian/Ubuntu
sudo rpm -Va # RHEL/FedoraAuditing — and a reminder that yours is recorded too.
sudo cat /home/usuario/.bash_historyGPG to protect a backup or a sensitive file.
gpg -c segredo.txt # encrypts with a password
gpg -d segredo.txt.gpg > segredo.txtWithout depending on a third-party site.
openssl rand -base64 32
head -c 32 /dev/urandom | base64