Permisos de Archivo en Linux: chmod y chown Explicados en la Práctica
Entiende los permisos rwx, ownership y cómo usar chmod y chown en Linux para resolver Permission denied en servidores VPS de forma definitiva.
Hiciste deploy de tu aplicación en la VPS, ejecutaste el comando, y la terminal devolvió Permission denied. O nginx retornó 403 incluso con el archivo ahí, visible en ls. Ese error es la puerta de entrada de todo developer que sale del entorno de escritorio y se encuentra con un sistema multi-usuario por primera vez. La buena noticia: el modelo de permisos de Linux es simple y predecible una vez entiendes tres cosas — quién es dueño, quién pertenece al grupo, y qué significa cada bit.
Esta guía es para quien ya vio chmod 777 en algún tutorial sin entender lo que realmente hace, o para quien necesita resolver el acceso a archivos sin abrir el servidor entero. Vamos a cubrir cómo leer la salida de ls -l, el significado de rwx para archivos y directorios, y cómo usar chmod (cambiar permiso) y chown (cambiar dueño) en escenarios reales de servidor web.
Tiempo estimado de ejecución: 20 a 30 minutos para leer, probar los comandos y fijar el modelo mental. Sales con la habilidad de diagnosticar cualquier error de permiso por tu cuenta.
Prerrequisitos
Necesitas una VPS Linux (Ubuntu 22.04+, Debian 12+ o cualquier distribución equivalente) con acceso SSH y privilegios sudo. Los ejemplos fueron probados en Ubuntu 24.04 LTS pero funcionan en cualquier distribución moderna. Recomendado tener un usuario no-root para practicar los comandos sin riesgo.
Ubuntu 24.04 LTS no-root + sudo ~/perms-test Cómo Linux representa los permisos
Todo archivo en Linux tiene tres entidades asociadas: el dueño (user/owner), el grupo (group) y el resto del mundo (others). Para cada una de esas tres entidades existen tres bits de permiso: r (read, lectura), w (write, escritura) y x (execute, ejecución). Nueve bits en total — tres para el dueño, tres para el grupo, tres para el resto.
Ejecuta ls -l en cualquier directorio para verlo renderizado:
ls -l /etc/hosts
La salida se parece a:
-rw-r--r-- 1 root root 220 May 14 10:22 /etc/hosts
Decodificando de izquierda a derecha:
-en el primer carácter indica que es un archivo regular. Para directorio seríad, para enlace simbólicol.rw-(caracteres 2-4): permisos del dueño. Lee y escribe, no ejecuta.r--(caracteres 5-7): permisos del grupo. Solo lectura.r--(caracteres 8-10): permisos del resto del mundo. Solo lectura.1: número de hard links.root root: dueño y grupo, respectivamente.220: tamaño en bytes.May 14 10:22: última modificación./etc/hosts: nombre del archivo.
Notación octal: el atajo que todo el mundo usa
Cada conjunto de tres bits (rwx) puede representarse con un dígito de 0 a 7. Cada bit tiene un peso:
- r = 4
- w = 2
- x = 1
Suma los pesos de los bits activos para obtener el dígito. Ejemplos prácticos:
| Octal | Símbolo | Significado |
|---|---|---|
| 7 | rwx | Lee, escribe, ejecuta |
| 6 | rw- | Lee y escribe |
| 5 | r-x | Lee y ejecuta |
| 4 | r— | Solo lectura |
| 0 | --- | Sin permisos |
Como cada archivo tiene tres conjuntos (dueño, grupo, mundo), el permiso completo es un número de tres dígitos. 644 es rw-r--r--. 755 es rwxr-xr-x. 600 es rw-------. Esa es la notación que vas a usar el 95% del tiempo con chmod.
El significado mágico de x en directorios
En un archivo, x significa ejecutar como programa. En directorio, x significa entrar al directorio (usar cd) y acceder a archivos por nombre. Sin x en un directorio, no puedes listar el contenido ni con r, y no puedes abrir archivos que están dentro aunque sepas el nombre.
Por eso el estándar clásico es 755 para directorios (todos pueden entrar y listar) y 644 para archivos (todos leen, solo el dueño escribe, nadie ejecuta). Invertir esto rompe el acceso de formas extrañas — archivo que existe pero “no existe” para otro usuario.
chmod: cambiando permisos en la práctica
chmod acepta dos notaciones: octal (la que mostramos arriba) y simbólica (u+x, g-w, o=r). La octal se usa más en producción porque es determinística — defines el estado final, no la transición.
Crea un directorio de práctica y un archivo de prueba:
mkdir ~/perms-test
cd ~/perms-test
echo "contenido de prueba" > archivo.txt
ls -l archivo.txtLa salida debe ser algo como -rw-rw-r-- (644 o 664 dependiendo del umask de tu shell). Vamos a manipular esto.
Aplica un permiso restrictivo en octal — solo el dueño lee y escribe:
chmod 600 archivo.txt
ls -l archivo.txtLa salida ahora muestra -rw-------. Nadie aparte del dueño puede ni siquiera leer. Ese es el estándar para la clave SSH privada (~/.ssh/id_rsa) y cualquier archivo con credenciales.
Vuelve a la lectura pública pero mantén la escritura restringida al dueño:
chmod 644 archivo.txt
ls -l archivo.txt-rw-r--r-- es el estado estándar para un archivo regular en un servidor web: el dueño escribe, todos leen.
Usa la notación simbólica para añadir ejecución solo al dueño sin tocar el resto:
chmod u+x archivo.txt
ls -l archivo.txtAhora aparece -rwxr--r--. La notación simbólica es útil cuando quieres modificar un bit específico sin tener que recordar el estado completo. Sintaxis: [ugoa][+-=][rwx]. u=user, g=group, o=others, a=all.
777 da rwx a todo el mundo — incluyendo cualquier proceso comprometido en la máquina. Si una aplicación web sufre RCE y el atacante consigue ejecutar comandos como www-data, los archivos 777 permiten que sobrescriba tu aplicación, plante un backdoor o borre todo. El síntoma “funcionó con 777” casi siempre indica que el ownership está mal — corrígelo con chown, no relajes el permiso.
chmod recursivo: -R con cuidado
La flag -R aplica el cambio a todo lo que está dentro de un directorio. Útil en situaciones específicas, peligrosa en otras:
chmod -R 755 ~/perms-test
El problema: esto añade x a todos los archivos, incluyendo los que no deberían ser ejecutables. En /var/www/site quieres 644 para archivos .html, .css, .php y 755 solo para los directorios. La forma correcta separa por tipo:
find /var/www/site -type f -exec chmod 644 {} \;
find /var/www/site -type d -exec chmod 755 {} \;
O, más eficiente en directorios grandes:
find /var/www/site -type f -print0 | xargs -0 chmod 644
find /var/www/site -type d -print0 | xargs -0 chmod 755
chown: cambiando dueño y grupo
chown cambia el dueño del archivo. Sintaxis básica:
chown nuevo_dueno archivo
chown nuevo_dueno:nuevo_grupo archivo
chown :nuevo_grupo archivo
Solo root (o un usuario con sudo) puede cambiar el dueño a otro usuario. Un usuario común no puede dar un archivo suyo a otra persona — esto evita ataques donde alguien empuja archivos a la cuota ajena.
Mira el escenario más común en producción: una aplicación web corriendo como www-data necesita leer/escribir en /var/www/site. Asigna dueño y grupo de una sola vez:
sudo chown -R www-data:www-data /var/www/siteAquí -R es apropiado porque quieres que todo el contenido pertenezca al usuario del servicio web. Este es el paso que resuelve el 80% de los Permission denied en deploy.
Confirma el cambio:
ls -l /var/www/site | headLa columna de dueño y grupo debe mostrar www-data www-data en todas las líneas.
En un script de deploy típico, el orden es: copiar archivos, ajustar ownership al usuario del servicio, ajustar permisos con find por tipo. Hard-codea esos comandos en tu script de provisión o playbook Ansible — no confíes en “voy a acordarme de ejecutarlo manualmente”. Un permiso olvidado es una fuente común de fallos intermitentes en entornos de staging.
Verificación: confirma que quedó correcto
Después de tocar permisos, valida tres cosas: el estado actual de los bits, el ownership, y que la aplicación realmente puede acceder.
ls -ld /var/www/site
ls -l /var/www/site | head
sudo -u www-data cat /var/www/site/index.html
El ls -ld muestra el permiso del propio directorio (sin -d, lista el contenido). El sudo -u www-data ejecuta el comando como si fueras el usuario del servicio web — si esto funciona sin error, tu aplicación también va a funcionar. Esa prueba es más confiable que mirar solo ls -l, porque involucra SELinux/AppArmor si están activos.
Para verificar que el chmod recursivo selectivo funcionó, cuenta archivos por permiso:
find /var/www/site -type f -perm 644 | wc -l
find /var/www/site -type f ! -perm 644
La segunda línea no debe retornar nada. Si retorna, son archivos con permiso distinto de 644 — investiga caso por caso antes de aplicar una corrección en masa.
Resolución de problemas
”Permission denied” incluso después de chmod 755
Verifica el ownership con ls -l. Si el archivo pertenece a root:root y el proceso corre como www-data, el permiso 755 solo ayuda en el bit other (último dígito). Pero en algunos sistemas con SELinux activo, contextos de seguridad extras bloquean el acceso independientemente del permiso Unix. Revisa con ls -Z y usa restorecon o chcon si es necesario.
”Operation not permitted” al ejecutar chown
No estás como root. Aunque seas dueño del archivo, transferirlo a otro usuario exige sudo. Añade sudo antes del comando o ejecuta como root.
Directorio padre sin permiso de ejecución bloquea el acceso
Escenario clásico: /home/user/public_html/site/index.html tiene 644, pero nginx retorna 403. El problema puede estar en /home/user — si esa carpeta no tiene x para el grupo/mundo, nginx (corriendo como www-data) no puede atravesar la ruta hasta el archivo. Solución: garantiza x en todos los directorios de la ruta, o usa una estructura /var/www donde los permisos están pensados para acceso web desde el principio.
Próximos pasos
Los permisos básicos cubren la mayoría de los escenarios, pero existen capas avanzadas que vale la pena explorar cuando el setup se vuelve más complejo:
- ACLs (Access Control Lists): permiten permisos por usuario específico además del trío dueño/grupo/mundo. Comandos
getfaclysetfacl. Útil cuando dos servicios necesitan acceso diferente al mismo archivo. - Bits especiales: setuid (4000), setgid (2000) y sticky bit (1000). El sticky bit en
/tmpes el ejemplo clásico — impide que el usuario A borre archivos del usuario B. - umask: el valor que define el permiso por defecto de los archivos nuevos. Configúralo en
/etc/profileo en el rc del shell del usuario para que los archivos creados ya nazcan con el permiso correcto. - Capabilities (setcap): alternativa moderna a setuid para dar un privilegio específico (ej: bind en puerto < 1024) sin dar root completo.
Si estás poniendo una aplicación en producción y necesitas un entorno con aislamiento real, acceso root y SSH dedicado para practicar estos comandos sin afectar producción, una VPS Hostini entrega eso con Ubuntu 24.04 LTS preinstalado y snapshot para que puedas revertir cualquier experimento.
Preguntas frecuentes
¿Cuál es la diferencia entre chmod 755 y chmod 775?
755 da rwx al dueño, r-x al grupo y r-x al resto del mundo (5 = 4+1, lectura y ejecución). 775 da rwx también al grupo (7 = 4+2+1). Usa 755 en código de aplicación donde solo el dueño escribe; 775 en directorios compartidos entre usuarios del mismo grupo, como uploads gestionados por dueño y proceso web.
¿Cuándo usar chmod -R y cuándo evitarlo?
Usa -R (recursivo) solo cuando sabes que todo el contenido del directorio debe tener el mismo permiso — generalmente en carpetas de log o caché. Evítalo en /var/www entero: los archivos necesitan 644 y los directorios 755, y -R 755 deja archivos ejecutables sin necesidad. Usa find con -type f y -type d por separado.
¿Por qué mi aplicación web muestra Permission denied incluso con chmod 777?
777 resuelve el bit de permiso pero no el ownership. Si el archivo pertenece a root y el proceso web corre como www-data, la lectura funciona (bit other), pero algunos sistemas bloquean por SELinux/AppArmor o por el open_basedir de PHP. Verifica el ownership con ls -l y los logs del servicio — 777 casi siempre es el síntoma equivocado.
¿Qué es el bit setuid (4000) y cuándo tiene sentido usarlo?
Setuid hace que el binario corra con privilegios del dueño en lugar del usuario que lo ejecutó. Se usa en /usr/bin/sudo y /usr/bin/passwd. En código propio es un vector de escalada de privilegios — nunca uses setuid en scripts shell y casi nunca en binarios custom; prefiere sudoers o capabilities (setcap).
¿Cuál es la diferencia entre chown y chgrp?
chown cambia el dueño (y opcionalmente el grupo, con sintaxis usuario:grupo). chgrp cambia solo el grupo. chown www-data:www-data archivo equivale a chown www-data archivo seguido de chgrp www-data archivo. Usa chown con la sintaxis combinada — son menos comandos y menos errores.
¿Tienen sentido permisos diferentes entre archivos y directorios?
Sí. En un directorio, el bit x significa entrar (cd), no ejecutar. Sin x en un directorio no puedes leer el contenido ni con r. Estándar sensato: 644 para archivos (rw-r--r--) y 755 para directorios (rwxr-xr-x). Los archivos ejecutables reciben 755 explícitamente; nunca 777.