Un servidor nunca está quieto: mientras lo miras, nginx sirve la web, cron espera a su hora, sshd atiende a quien entra y, a veces, un script se queda colgado comiéndose la CPU. Cada programa en marcha es un proceso, y los que tienen que estar siempre funcionando son servicios, que vigila systemd.
En este tema aprendes a ver qué se ejecuta y cuánto gasta, a parar lo que sobra (y solo eso), a
arrancar y habilitar servicios y a leer el diario para saber qué pasó. Los ejemplos usan
srv-tienda, el servidor de la tienda online, una mañana cualquiera.
Qué se está ejecutando: ps
Cada proceso tiene un número, el PID, y un dueño. ps, sin más, enseña los de tu terminal:
tu bash y el propio ps:
tecnico@srv-tienda:~$ ps PID TTY TIME CMD 2600 pts/2 00:00:00 bash 2601 pts/2 00:00:00 ps
Para verlos todos, de todos los usuarios, ps aux (la forma BSD, sin guion) o ps -ef (la
de siempre de Unix). El primero es el 1, /sbin/init, que en Ubuntu es systemd: el padre de
todo lo demás.
tecnico@srv-tienda:~$ ps aux | head -3 USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.3 166972 11984 ? Ss Oct10 0:12 /sbin/init root 88 0.0 0.3 35388 12688 ? Ss Oct10 0:03 /lib/systemd/systemd-journald
Las columnas que más se miran: el usuario y el PID; %CPU y %MEM; TTY, la
terminal (? si no tiene: los servicios); STAT, el estado (S durmiendo, R corriendo, T
parado); START, cuándo empezó, y TIME, cuánta CPU lleva gastada en total. Para buscar uno,
grep o, mejor, pgrep -a, que da el PID y la orden entera:
tecnico@srv-tienda:~$ pgrep -a nginx 401 nginx: master process /usr/sbin/nginx -g daemon on; master_process on; 402 nginx: worker process 403 nginx: worker process
nginx son tres procesos: el maestro (de root, que lee la configuración) y dos trabajadores
(de www-data, que atienden las peticiones). ps -ef enseña además el PPID, el padre de
cada uno.
Ejercicio
Lo que se está ejecutando: ps
Te conectas a srv-tienda para echar un vistazo. Antes de tocar nada, mira qué se está ejecutando.
Preparando la máquina…
Cuánto trabaja la máquina: top, uptime y free
top es una foto de la máquina: arriba, cuánto lleva encendida, la carga y la memoria;
abajo, los procesos, primero los que más CPU gastan. (En un servidor de verdad top se va
actualizando hasta que pulsas q; aquí es una foto, como top -b -n 1.)
tecnico@srv-tienda:~$ top | head -12 top - 09:20:06 up 3 days, 5:00, 1 user, load average: 1.01, 0.99, 0.90 Tasks: 15 total, 2 running, 13 sleeping, 0 stopped, 0 zombie %Cpu(s): 49.5 us, 0.8 sy, 0.0 ni, 49.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st MiB Mem : 3921.0 total, 2462.3 free, 280.9 used, 1177.7 buff/cache MiB Swap: 2048.0 total, 2048.0 free, 0.0 used. 3640.0 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 2417 marta 20 0 31248 24876 16418 R 99.0 0.6 118:53.94 python3 402 www-data 20 0 55852 5536 3653 S 0.0 0.1 0:05.54 nginx 403 www-data 20 0 55852 5536 3653 S 0.0 0.1 0:05.54 nginx 401 root 20 0 55284 1648 1087 S 0.0 0.0 0:02.77 nginx 1 root 20 0 166972 11984 7909 S 0.0 0.3 0:12.40 systemd
Aquí salta a la vista: un python3 de marta al 99 % de CPU, que lleva casi dos horas de CPU
(TIME+). La carga (load average) son tres medias, de 1, 5 y 15 minutos, de cuántos
procesos quieren CPU a la vez: en una máquina de 2 núcleos, 1.00 es medio llena y 2.00, llena.
uptime da esa misma primera línea, y free -h la memoria en unidades que se leen (-h):
tecnico@srv-tienda:~$ free -h total used free shared buff/cache available Mem: 3.8Gi 281Mi 2.4Gi 21Mi 1.2Gi 3.6Gi Swap: 2.0Gi 0B 2.0Gi
La columna que importa es available: lo que queda de verdad (Linux usa la memoria libre como
caché, buff/cache, y la suelta cuando hace falta). Para ordenar ps por CPU:
ps aux --sort=-%cpu (el - es de más a menos).
Ejercicio
Cuánto trabaja la máquina: top, uptime y free
Dicen que la tienda va lenta esta mañana. Mira cuánto trabaja srv-tienda y quién se lleva la CPU.
Preparando la máquina…
Parar un proceso: kill y las señales
Para parar un proceso se le manda una señal con kill y su PID. La de siempre es SIGTERM
(15): “termina, por favor”, y el programa cierra lo suyo antes de irse. Solo puedes mandarlas a
tus procesos; a los de otro, con sudo:
tecnico@srv-tienda:~$ kill 2417 bash: kill: (2417) - Operation not permitted
tecnico@srv-tienda:~$ sudo kill 2417
tecnico@srv-tienda:~$ pgrep -a python3 2417 python3 informe.py --trimestre
Sigue ahí: un programa colgado puede no hacer caso de SIGTERM. Entonces, SIGKILL (9), que no se puede ignorar: el núcleo lo quita de en medio, sin dejarle cerrar nada.
tecnico@srv-tienda:~$ sudo kill -9 2417
kill -9, kill -KILL y kill -s KILL son lo mismo; kill -l lista todas las señales.
pkill y killall buscan por nombre en lugar de por PID (pkill -f mira la orden entera).
Ejercicio
Parar un proceso desbocado: kill
Un script de marta, informe.py, lleva dos horas con la CPU al 100 % y la tienda va a pedales. Marta dice que lo pares, que ya lo lanzará esta noche.
Preparando la máquina…
Los servicios: systemctl
Lo que tiene que estar siempre en marcha (nginx, sshd, cron, una API) no se lanza a mano: es un
servicio, y lo arranca y vigila systemd. Se gobiernan con systemctl, y lo primero es
siempre mirar su estado:
tecnico@srv-tienda:~$ systemctl status nginx ● nginx.service - A high performance web server and a reverse proxy server Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled) Active: active (running) since Sat 2026-10-10 04:20:00 UTC; 3 days ago Docs: man:nginx(8) Main PID: 401 (nginx) Tasks: 3 (limit: 4556) Memory: 12.4M CPU: 13.861s CGroup: /system.slice/nginx.service ├─401 nginx: master process /usr/sbin/nginx -g daemon on; master_p… ├─402 nginx: worker process └─403 nginx: worker process Oct 10 04:20:00 srv-tienda systemd[1]: Starting A high performance web server and a reverse proxy server... Oct 10 04:20:00 srv-tienda systemd[1]: Started A high performance web server and a reverse proxy server.
Se lee de arriba abajo: el punto (● verde en marcha, ○ parado, × rojo fallido); Loaded, de qué archivo sale y si está habilitado; Active, su estado y desde cuándo; Main PID, su proceso principal; CGroup, todos sus procesos, y al final, sus últimas líneas del diario.
Para cambiarlo, con sudo: start, stop, restart (parar y arrancar) y reload (que relea
la configuración sin parar, si el programa sabe). systemctl is-active nginx responde solo
active o inactive (útil en scripts).
Ejercicio
Los servicios: systemctl
nginx no se lanza a mano: es un servicio, y lo vigila systemd. Toca pararlo un momento para una revisión.
Preparando la máquina…
Que arranque sola: enable
Arrancar un servicio y habilitarlo son cosas distintas. start lo pone en marcha
ahora; enable hace que arranque con la máquina (crea un enlace en
multi-user.target.wants). Un servicio arrancado a mano y sin habilitar funciona… hasta el
próximo reinicio:
tecnico@srv-tienda:~$ systemctl status tienda ○ tienda.service - Tienda online (API) Loaded: loaded (/etc/systemd/system/tienda.service; disabled; vendor preset: enabled) Active: inactive (dead)
tecnico@srv-tienda:~$ sudo systemctl enable --now tienda Created symlink /etc/systemd/system/multi-user.target.wants/tienda.service → /etc/systemd/system/tienda.service.
--now hace las dos cosas a la vez (y disable --now las deshace). systemctl is-enabled tienda
dice enabled, disabled o static (una unidad sin sección [Install], que no se habilita:
la lanza otra cosa, como cron).
Ejercicio
Que arranque sola: enable
La API de la tienda (tienda.service) está parada desde el último reinicio. Que vuelva, y que la próxima vez arranque sola.
Preparando la máquina…
Las unidades y daemon-reload
Cada servicio se describe en un archivo de unidad: los de los paquetes en
/lib/systemd/system, los tuyos en /etc/systemd/system. systemctl cat lo enseña:
tecnico@srv-tienda:~$ systemctl cat informes # /etc/systemd/system/informes.service [Unit] Description=Informe diario de ventas [Service] Type=oneshot ExecStart=/usr/local/bin/informe-diario.sh
ExecStart es lo que se ejecuta; User, con qué usuario; Type=oneshot, que hace su trabajo y
termina (una copia, un informe), y [Install], a qué se engancha al habilitarlo.
Qué ha pasado: el diario
Los servicios apuntan lo que les pasa en el diario de systemd, y journalctl lo lee. Lo
primero, qué ha fallado:
tecnico@srv-tienda:~$ systemctl --failed UNIT LOAD ACTIVE SUB DESCRIPTION ● informes.service loaded failed failed Informe diario de ventas LOAD = Reflects whether the unit definition was properly loaded. ACTIVE = The high-level unit activation state, i.e. generalization of SUB. SUB = The low-level unit activation state, values depend on unit type. 1 loaded units listed.
Y luego, su parte del diario, con -u (de unit):
tecnico@srv-tienda:~$ journalctl -u informes Oct 13 06:00:00 srv-tienda systemd[1]: Starting Informe diario de ventas... Oct 13 06:00:00 srv-tienda informe-diario.[404]: Generando el informe diario de ventas Oct 13 06:00:00 srv-tienda informe-diario.[404]: ls: cannot access '/srv/informes/salida': No such file or directory Oct 13 06:00:00 srv-tienda systemd[1]: informes.service: Main process exited, code=exited, status=2/INVALIDARGUMENT Oct 13 06:00:00 srv-tienda systemd[1]: informes.service: Failed with result 'exit-code'. Oct 13 06:00:00 srv-tienda systemd[1]: Failed to start Informe diario de ventas.
Ahí está: a las 6 no existía la carpeta de salida. Lo que escribe el programa sale con su nombre
(informe-diario.[404]), y lo de systemd, con el suyo. Lo más útil de journalctl: -n 20 las
últimas 20 líneas; -p err solo errores (y lo más grave); --since "2026-10-13 05:00" desde una
hora; -x, con una explicación de cada mensaje de systemd, y -e, saltar al final
(journalctl -xeu nginx, lo que te sugiere systemctl cuando algo falla).
El núcleo también apunta lo suyo: si la máquina se queda sin memoria, elige un proceso y lo mata
(Out of memory: Killed process 412 (python3)…, que sale con journalctl -p err). Si era el
principal de un servicio, este se queda en failed (Result: signal), con status=9/KILL, y no
vuelve solo: hay que arrancarlo otra vez con sudo systemctl start.
Para leer el diario entero hace falta estar en el grupo adm (o systemd-journal), o usar
sudo: sin ello, solo ves lo tuyo.
Ejercicio
Qué ha pasado: journalctl
Esta mañana no ha llegado el informe diario de ventas. Algo falló a las 6: averigua qué.
Preparando la máquina…