ChmodCalc

chmod 600 — rw-------

The secret-file mode. Nothing is granted outside the owner, which is not merely good hygiene here — it is the literal condition OpenSSH tests before it will use a private key, and the calculator below will tell you the moment you break it.

chmod — file mode
whoreadwriteexecutedigit
Owner6
Group0
Others0
special bits — the fourth digit

ls -l → -rw-------

chmod 600 ~/.ssh/id_ed25519
chmod u=rw,g=,o= ~/.ssh/id_ed25519

Absolute symbolic form — identical result, and the only form that can name the setuid, setgid and sticky bits.

what 600 grants on this file

  • Ownerrw-read the contents and change the contents
  • Group---nothing
  • Others---nothing

programs that refuse this mode

  • nginx

    open() "~/.ssh/id_ed25519" failed (13: Permission denied)

    fix: chmod 644 ~/.ssh/id_ed25519

  • Git

    hint: The '~/.ssh/id_ed25519' hook was ignored because it's not set as executable.

    fix: chmod +x ~/.ssh/id_ed25519

  • Any POSIX shell (bash, dash, zsh, sh)

    ~/.ssh/id_ed25519: Permission denied (exit status 126)

    fix: chmod +x ~/.ssh/id_ed25519

The whole owner-only family

600 belongs to a small, closed set: the modes where the group digit and the other digit are both zero. There are 8 of them out of 512, which is few enough to print entirely — and printing them entirely is more useful than repeating "use 600", because it shows what the neighbouring choices actually do.

Every three-digit mode that grants nothing to the group and nothing to others. Filtered from all 512 modes; the "OpenSSH accepts" column is the client's own private-key test evaluated on each row.
Mode Symbolic Owner reads Owner writes Owner runs OpenSSH accepts as a key What it is for
000 --------- no no no yes nothing is readable — even the owner must chmod before reading
100 --x------ no no yes yes run-only: a compiled binary can work, a shell script cannot
200 -w------- no yes no yes write-only: you can append, and you cannot read back what you wrote
300 -wx------ no yes yes yes write and run without read — the same shell-script problem
400 r-------- yes no no yes the same thing, protected against your own accidental overwrite
500 r-x------ yes no yes yes a private script you do not want to edit by mistake
600 rw------- yes yes no yes a private key, a token file, a .env — the owner reads and edits it
700 rwx------ yes yes yes yes a private script or directory

Every row says yes in the OpenSSH column. That is the finding and not an oversight: the client's test is (st.st_mode & 077) != 0, which looks only at the group and other digits. It does not care what the owner may do. A key at 000 passes the permission check and then fails to open, which is a different error with a different message — worth knowing when you are staring at one.

Source: OpenSSH (portable), authfile.c — sshkey_perm_ok(). C source read directly from the project’s master branch.

600 versus 400

Both are accepted everywhere a private key is accepted. The difference is whether you can overwrite the file without a chmod first. For a key you generated once and will never edit, 400 costs nothing and turns "I accidentally redirected output over my key" from a catastrophe into an error message. For a .env file you edit weekly, 600 is the practical choice. Neither protects the file from deletion — that is a property of the directory, not of the file.

The three rows below 600 that look like typos and are not

200 is write-without-read. You can append to the file and you cannot read back what you wrote — genuinely useful for a log a process should not be able to inspect, and confusing the first time you meet it. 100 is run-without-read, which works for a compiled binary and does not work for a shell script, because the shell must read a script to interpret it; the error you get is "Permission denied" issued by the interpreter, with the kernel never involved. And 000 means even the owner must chmod the file before touching it, which is a perfectly legitimate way to park something you want to be deliberate about.

Where 600 is the right answer

The contexts below are the ones where a secret file lives, with the basis for each value stated. Note the honesty of the .env row: unlike an SSH key, nothing enforces a mode on it. No loader that this site checked refuses to read a world-readable .env. The reason to use 600 there is that everything else on a shared host can read a 644 file, not that a program will complain — which is a weaker claim than the SSH one, and is labelled as such.

Secret files, and how strong the recommendation actually is.
Where Mode Basis The rule, and why
.env and similar secret files

file

600

rw-------

convention

No enforcement found. None of the loaders whose behaviour this site verified refuses to read a world-readable .env — unlike an SSH private key, nothing stops you.

The reason to use 600 is that everything else on a shared host can read a 644 file, not that a program will complain. If your deployment needs the application user to read it and the file is owned by a deploy user, 640 with a shared group is the usual shape.

No source cited, because none was found. That is the whole content of a “convention” row.

/etc/sudoers.d/<name>

file

440

r--r-----

enforced

sudo_check_secure(): (sb->st_mode & S_IWOTH) → "%s is world writable"; ISSET(sb->st_mode, S_IWGRP) && (gid == (gid_t)-1 || sb->st_gid != gid) → the group-writable refusal. Either one and sudo refuses to run.

Two mode conditions, not one. World-writable is rejected outright; group-writable is rejected as well unless the file’s group is the sudoers group sudo was built to expect. 440 is the conventional value that clears both. 400 clears them too and grants strictly less, so 440 is simply what people settle on here — no source makes it a minimum. Edit these with visudo, which validates before replacing the file.

sudo — lib/util/secure_path.c · plugins/sudoers/sudoers.c

  • enforced the program refuses to proceed otherwise — the condition is quoted from its source
  • convention no enforcement and no documented requirement was found — this is what people do
The SSH set, where the enforcement is real and quoted from source.
Where Mode Basis The rule, and why
~/.ssh (the directory)

directory

700

rwx------

documented

ssh(1) FILES: "There is no general requirement to keep the entire contents of this directory secret, but the recommended permissions are read/write/execute for the user, and not accessible by others."

What sshd actually enforces is weaker: safe_path() rejects the directory only when (st_mode & 022) is non-zero, so 755 passes the login check. 700 is the documented recommendation, not the enforced floor — a distinction most cheat-sheets collapse.

OpenSSH (portable) — ssh.1 — FILES · misc.c

~/.ssh/id_ed25519, id_rsa (private keys)

file

600

rw-------

enforced

(st.st_uid == getuid()) && (st.st_mode & 077) != 0 → the key is ignored

The mask is 077, so every bit outside the owner triad fails, read included. Exactly 8 of the 512 three-digit modes satisfy it, and 600 is the only one of those that is both readable and writable by its owner without granting execute. (200 is writable without execute too, but you cannot read the key back.)

OpenSSH (portable) — authfile.c · ssh.1 — FILES

~/.ssh/authorized_keys (on the server)

file

600

rw-------

documented

ssh(1) FILES: "This file is not highly sensitive, but the recommended permissions are read/write for the user, and not accessible by others." Enforced floor: (st_mode & 022) == 0 when StrictModes is on.

Public keys are public, so readability is not the risk; writability is. Anyone who can append a line to this file owns the account.

OpenSSH (portable) — ssh.1 — FILES · misc.c

~/.ssh/config

file

600

rw-------

enforced

(sb.st_mode & 022) != 0 → fatal("Bad owner or permissions on %s")

ssh exits here; it does not warn and carry on. A writable client config lets somebody else add a ProxyCommand and route your sessions through a machine of their choosing.

OpenSSH (portable) — readconf.c · ssh.1 — FILES

  • enforced the program refuses to proceed otherwise — the condition is quoted from its source
  • documented the project’s own manual recommends this value

Setting it without a mistake

The absolute form is chmod 600 ~/.ssh/id_ed25519. The relative form that strips everyone else without touching the owner's bits is chmod go= ~/.ssh/id_ed25519 — an empty permission list after = clears the class entirely, which is exactly what the 077 mask is testing for. On a whole directory of keys:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_* ~/.ssh/config
chmod 644 ~/.ssh/*.pub

Public keys can stay readable; they are public. What must not be loose is the private key and the client config, and those two use different masks — 077 for the key, 022 for the config — which the SSH key permissions page sets out check by check.

## Questions people actually ask

What does chmod 600 mean?

rw------- — the owner can read and write, and nobody else can do anything at all. It is the mode for anything whose contents are a secret: SSH private keys, API token files, .env files, database credentials.

Why does SSH insist on 600 for a private key?

It does not insist on 600 specifically — it insists that the group and others have no bits, which is the test (st.st_mode & 077) != 0 in OpenSSH's sshkey_perm_ok(). That leaves 8 acceptable modes out of 512. Two of those 8 are both readable and editable by you — 600 and 700 — and 600 is the less privileged, because 700 additionally makes the key executable, which a key has no use for. 400 works too and is arguably better for a key you never intend to change.

Is 600 enough to protect a secret?

Against other unprivileged accounts on the same machine, yes. Against root, no — root bypasses the permission bits entirely, and so does anyone who can read the raw disk or a backup of it. The mode is one layer: it stops the other tenants of a shared box, the web server user, and a compromised unrelated service. It does not stop the machine's administrator, and it is not a substitute for encrypting the secret at rest.

What is the difference between 600 and 640?

The group read bit. 640 lets every member of the owning group read the file, which is exactly what you want when one service account needs the credential and you would rather not transfer ownership — put that account in a dedicated group of its own and use 640. It is exactly what you do not want when the owning group is something broad like users or staff, because then "the group" is everybody.

Can I use 600 on a directory?

You can set it, and the result is close to useless: without the execute bit you cannot enter the directory or reach anything inside it, so you get a list of names and nothing more. The private-directory mode is 700, which adds the search bit the owner needs.

Does 600 stop a file from being deleted?

No, and this surprises people regularly. Deleting a file is a write to the directory that contains it, not to the file, so a 600 file inside a world-writable directory can be removed by anyone. That is precisely the hole the sticky bit closes — see chmod 777 for how 1777 differs from 777.

elsewhere on this site

other tools we run