777 means every account on the box can rewrite the file·OpenSSH ignores a key it can read at 0777·sudo quits on a world-writable sudoers·/tmp is the one place it belongs

The octal, in 30 seconds

Three digits, three classes of user: owner, group, everyone else. Each digit is a sum of read 4, write 2, execute 1. So 7 is 4+2+1, all three, and chmod 777 hands full access to every account on the machine.

ModeReads asWhere it belongs
644rw-r--r--Ordinary files. Owner writes, everyone reads
755rwxr-xr-xDirectories and executables
600rw-------Secrets: SSH keys, .env files, tokens
2775rwxrwsr-xShared directory, setgid so new files inherit the group
1777rwxrwxrwt/tmp. World-writable, sticky bit prevents deleting other people’s files

Two things the tables in other guides skip. A numeric chmod sets the whole mode, so chmod 777 also clears the setuid, setgid and sticky bits that were there before; that is not a side effect people expect and it is how chmod 777 /tmp quietly removes the sticky bit. And the default umask of 022 that most distributions ship is why new files come out 644 and new directories 755: the kernel starts from 666 for files (never 777, ordinary files are not born executable) and masks the write bits off for group and other.

The bit everyone misreads: w on a directory

On a file the meanings are obvious. On a directory they are not, and this is where 777 does its real damage.

  • r lets you list the names inside.
  • x lets you traverse into it and access a known path through it. A directory with x but no r is a directory whose contents you can use but not enumerate.
  • w lets you create, rename and delete entries.

That last one is the part that catches people. Deleting a file is a modification of the directory, not of the file, so write permission on the directory lets any user remove a file inside it no matter what the file’s own mode says. A 600 file owned by root, in a 777 directory, can be deleted by any account on the box. It can also be replaced: delete, then create a new file with the same name and your own contents. Everything reading that path now reads your file.

Combine that with a path something privileged executes, a cron script, a systemd unit, a binary in PATH, and world-writable stops being a tidiness issue.

Software that refuses to run at all

The strongest argument against 777 is not theoretical. A good deal of software checks the mode of its own files and declines to proceed, because loose permissions mean the file cannot be trusted. Each of these produces an error that looks nothing like a permissions problem to whoever is reading the logs.

  • OpenSSH, client side. It tests the group and other bits of a private key and refuses if any are set, with the @@@ WARNING: UNPROTECTED PRIVATE KEY FILE! @@@ banner, “Permissions 0777 are too open”, and “This private key will be ignored”. Note the check is on those six bits, so 600 and 400 pass while a merely group-readable 640 already fails. This is also why keys sitting on a Windows drive mounted into WSL misbehave: the mount reports 0777 for every file, so every key on it looks unprotected.
  • OpenSSH, server side. StrictModes is on by default and makes sshd ignore ~/.ssh/authorized_keys if the home directory, the .ssh directory or the file itself is group- or world-writable. The client just falls back to a password prompt with no explanation. The reason is in the server log: “Authentication refused: bad ownership or modes for directory”. We have watched this eat an afternoon after someone loosened a home directory to make a deploy script work.
  • sudo. A world-writable sudoers file would let anyone grant themselves root, so sudo checks and quits: “/etc/sudoers is world writable”, followed by “no valid sudoers sources found, quitting”. The file is meant to be 0440 and owned by root. Same family of check on the sudo binary itself, which must be owned by uid 0 with the setuid bit set.
  • PostgreSQL. The server refuses to start if its data directory is accessible to group or world: FATAL: data directory has invalid permissions, with the detail line naming the two acceptable modes, u=rwx (0700) or u=rwx,g=rx (0750). The group-readable variant only became legal in PostgreSQL 11 (2018) via initdb’s --allow-group-access; before that it was 0700 or nothing. The directory holds the raw table files and the role password hashes, so anything readable there is a full bypass of every SQL-level permission you configured, and how those password hashes were computed becomes your last line of defence.
  • cron. Files dropped into /etc/cron.d have to be owned by root and must not be group- or other-writable, otherwise cron skips them, logging a terse “WRONG FILE OWNER” if you are lucky. The job simply never runs, which is a miserable thing to debug and joins the other classic cron traps.
  • Apache suexec. Refuses to execute a CGI script that is writable by group or other, on the grounds that a world-writable script is anybody’s script.

Why 777 gets typed in the first place

Nobody sets 777 because they want everyone to have write access. It happens for two reasons, and both are worth naming.

The first is a shared directory that genuinely needs more than one identity writing to it: a web server writing uploads while a deploy user writes releases, a CI runner and a service account sharing a cache. The permissions model has an answer for this (group ownership plus setgid, or an ACL) and 777 is the version you reach for when you do not want to look it up at 23:00.

The second is debugging by escalation. Something returns EACCES, you widen the permissions until it stops, and 777 is the end of that road. The trouble is that it works often enough to be reinforcing, and the times it does not work are exactly the cases above, where the software wanted tighter permissions. So the tool that usually silences the error occasionally causes it, which is about the worst possible property for a debugging habit.

A related trap: permissions you set by hand are usually invisible to your version control. Git records a single permission bit per file, the executable bit, storing blobs as 100644 or 100755 and nothing more. Your careful chmod never leaves your machine, in much the same way that .gitignore quietly fails to affect files git has already tracked. Permissions belong in whatever provisions the machine, not in tribal memory.

What the real fix looks like

Almost every 777 is a case of the wrong owner or the wrong group. Fix that first, then set conservative modes.

  • Ownership before permissions. chown -R deploy:www-data /srv/app and then 755/644 solves most of what 777 was papering over. If the web server only reads, it does not need to be the owner at all.
  • setgid on shared directories. chmod 2775 dir makes every new file and subdirectory inherit the directory’s group instead of the creator’s primary group. Without it, two users writing into the same folder produce files the other cannot touch, which is the exact pain that leads to 777 half an hour later.
  • umask for the processes involved. Group-writable results need umask 002, and setting it in the service unit or the user’s profile is more durable than chmod-ing after the fact. Red Hat systems already run non-root users at 002 thanks to user private groups; Debian and Ubuntu default to 022.
  • ACLs when two groups genuinely need access. setfacl -R -m g:www-data:rX -m d:g:www-data:rX /srv/app grants one extra group access, and the d: entries make it the default for anything created later. ACLs are the right tool the moment the answer to “which single group owns this” is “neither”.
  • Uploads are never executable. 644 at most, ideally stored outside the document root and served by application code. The historical PHP disaster was not that upload directories were 777, it was that they were 777 and inside a directory the web server would happily execute.

/tmp, and the one place 777 is fine

Run ls -ld /tmp and you get drwxrwxrwt. Every user can write, and the trailing t is the sticky bit, which changes the deletion rule: inside a sticky directory you may only remove or rename an entry if you own the file, own the directory, or are root. That single flag is what makes an everyone-writable directory workable, because without it any user could delete any other user’s temporary files, including the lock files and sockets that services depend on.

The name is a fossil. In early Unix the same bit on an executable told the kernel to keep the program’s text segment in swap so it would reload faster, which stopped being useful decades ago; Linux ignores it on regular files entirely and only implements the directory behaviour. If you ever type chmod 777 /tmp you drop it, since the numeric form clears the special bits, and you should put it back with chmod 1777 /tmp immediately.

Root in a container is the same mistake

Docker runs your process as uid 0 unless the image sets a USER, and the uid inside the container is the same number the host kernel sees. Without user namespace remapping, root in the container is root on any bind-mounted host path, which is why files written by a container so often end up owned by root on the host, and why the fastest fix (chmod 777 on the mount) gets applied so often.

It is the same reflex as the 777 above: widen access until the error stops, rather than lining the identities up. The tidier version is a fixed non-root uid in the image, the same uid owning the host directory, and --user or a Kubernetes securityContext with runAsNonRoot: true to keep it that way. It is fifteen minutes of setup that also removes an entire class of container escape from your risk list.

After a chmod -R 777 has already happened

If it was a project directory: find . -type d -exec chmod 755 {} + and find . -type f -exec chmod 644 {} +, then put the executable bit back on the handful of files that need it. Any git repository will tell you which those are, since the executable bit is the one thing it does track.

If it was the system root, the honest advice is to stop and reach for backups. A recursive numeric chmod strips setuid from /usr/bin/sudo and /usr/bin/passwd, so sudo answers everything with “must be owned by uid 0 and have the setuid bit set” and you have no way back to root; key-based SSH is refused by StrictModes at the same moment. On a Debian or Ubuntu box you can reconstruct most modes from the package database with dpkg --verify, on Red Hat with rpm -Va --nomtime, but only from a rescue environment, and neither covers files that were never packaged. Watching this go wrong teaches one lesson in two parts: never run a recursive chmod without an absolute path you have read twice, and never with a trailing slash you typed in a hurry.

Common chmod and permission questions

What does chmod 777 actually do?

It gives read, write and execute permission to the file’s owner, to everyone in the file’s group, and to every other user account on the system. The three digits are the three classes in that order, and each digit is the sum of read (4), write (2) and execute (1), so 7 is all three. On a directory the write bit is the dangerous one, because it lets any user create and delete entries inside, including deleting files they cannot read and could never modify.

Is chmod 777 ever safe?

Only on a directory that also has the sticky bit, which is why /tmp is mode 1777 and not 777. The sticky bit restricts deletion inside a world-writable directory to the owner of each file, the owner of the directory and root, which is the missing piece that makes shared write access workable. Everywhere else 777 either grants more than intended or actively breaks software that inspects permissions before trusting a file, so the honest answer is no.

What is the difference between chmod 755 and 777?

755 gives the owner full access while everyone else can read and execute but not write; 777 adds write access for the group and for every other account on the machine. For directories, 755 means others can enter and list the contents but cannot add or delete entries, which is what you want for almost any directory a web server reads. The usual sane pair is 755 for directories and 644 for files, matching the default umask of 022 that most distributions ship.

Why does SSH say my private key permissions are too open?

Because OpenSSH checks the group and other bits of the key file and refuses to use the key if any of them are set, printing the “UNPROTECTED PRIVATE KEY FILE” banner and ignoring the key. A private key readable by other accounts on the machine is a private key you have to assume is copied, so the client will not proceed. chmod 600 on the key and 700 on ~/.ssh fixes it. The same check exists on the server: with StrictModes on, sshd ignores authorized_keys if the home directory, .ssh or the file is group- or world-writable.

What permissions should a web upload directory have?

755 on the directory, owned by a user you control, with the web server’s user added through group ownership or an ACL if it needs to write. The pattern that works is: the directory group is the web server’s group, the mode is 2775 so the setgid bit makes new files inherit that group, and uploaded files land as 644. Uploaded files must never be executable, and ideally they live outside the document root entirely and get served by application code.

Does git track file permissions?

Git stores exactly one permission bit, the executable bit, so a blob is recorded as either 100644 or 100755 and nothing else. A chmod 777 in your working tree produces no diff and never travels to a colleague, which means permissions cannot be part of your deployment through git alone. If even the executable bit causes noise, usually on a Windows or network filesystem, core.fileMode=false tells git to ignore it.

What is the sticky bit?

The sticky bit is a mode flag, shown as a t in the last position of ls output, that changes deletion rules inside a world-writable directory: only the file’s owner, the directory’s owner and root can remove or rename an entry. The name comes from its original meaning in early Unix, where it kept a program’s text segment in swap for faster reloads, a behaviour Linux dropped long ago. Today the directory semantics are the entire point, and /tmp is the canonical example at mode 1777.

What happens if you run chmod -R 777 /?

The system keeps running until you try to do anything privileged, and then it collapses in an unrecoverable way. A numeric chmod clears the setuid bit, so sudo loses it and answers every command with “sudo must be owned by uid 0 and have the setuid bit set”, which locks you out of the one tool that would fix it. sshd stops accepting key logins because StrictModes rejects the world-writable home directories, and PostgreSQL refuses to start. Restore from backup or boot rescue media; there is no clean in-place repair.