mkdir creates directories. It’s simple, but two options, -p and -m, cover most real-world needs, and brace expansion turns it into a quick way to build whole directory trees.
mkdir [options] DIRECTORY...
Create a directory
mkdir backups
mkdir /srv/app
mkdir logs cache tmp # several at once
Relative paths are created inside your current directory (pwd).
Create nested directories with -p
mkdir -p /var/www/example.com/public
Without -p, this fails if /var/www/example.com doesn’t exist yet. -p (parents) does two things:
- creates every missing parent directory;
- doesn’t fail if the directory already exists.
The second point makes mkdir -p the standard choice in scripts, Dockerfiles and deployment steps, because running it twice is harmless.
Set permissions at creation with -m
mkdir -m 700 ~/.secrets # only you can access it
mkdir -m 2775 /srv/shared # group-writable, setgid so new files inherit the group
Without -m, the new directory’s mode is 777 minus your umask. That’s usually 755 (umask 022) or 775 (umask 002). Note that with -p, -m applies only to the final directory. The parents get the default mode.
The octal modes are explained in chmod and Linux permissions.
Build directory trees with brace expansion
The shell expands braces before mkdir runs:
mkdir -p project/{src,tests,docs}
mkdir -p /srv/app/{releases,shared/{storage,logs},config}
mkdir -p backups/2026-{01..12}
The second line creates:
/srv/app/releases
/srv/app/shared/storage
/srv/app/shared/logs
/srv/app/config
Don’t put spaces inside the braces. {src, tests} won’t expand.
Verbose output
mkdir -pv /srv/app/shared/logs
# mkdir: created directory '/srv/app'
# mkdir: created directory '/srv/app/shared'
# mkdir: created directory '/srv/app/shared/logs'
Ownership: directories for services
Directories you create with sudo mkdir belong to root. A web server or app user usually needs ownership of its own directories:
sudo mkdir -p /var/www/example.com/public
sudo chown -R deploy:www-data /var/www/example.com
Or create and set ownership and mode in one step with install:
sudo install -d -o deploy -g www-data -m 2775 /var/www/example.com/public
install -d is handy in scripts and provisioning. For directories that services need under /run or /var/lib, systemd can create them for you: RuntimeDirectory=, StateDirectory= and LogsDirectory= in the unit file. See chown.
Temporary directories
For scratch space in scripts, use mktemp instead of a fixed name:
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
This avoids name clashes and race conditions, and the trap cleans up when the script exits.
Common errors
mkdir: cannot create directory 'x': No such file or directory — a parent directory is missing. Add -p.
mkdir: cannot create directory 'x': File exists — something with that name already exists. It might be a file, not a directory. Check with ls -ld x. Use -p if an existing directory is fine.
mkdir: cannot create directory 'x': Permission denied — you can’t write to the parent directory. Use sudo, or create it somewhere you own. Then fix ownership if a service needs it.
No space left on device even though df -h shows free space — the filesystem may be out of inodes. Check with df -i. See disk full troubleshooting.
Read-only file system — the filesystem was remounted read-only, often after disk errors. Check dmesg and the provider console.
Removing directories
rmdir dir removes an empty directory. rmdir -p a/b/c removes c, then b, then a, as long as each is empty. For directories with contents, use rm -r. See the rm command.
Quick reference
| Task | Command |
|---|---|
| Create dir | mkdir name |
| Nested, no error if exists | mkdir -p a/b/c |
| With permissions | mkdir -m 750 name |
| Several / tree | mkdir -p app/{src,logs} |
| Show what was created | mkdir -pv path |
| With owner and mode | install -d -o user -g group -m 755 path |
| Temporary dir | mktemp -d |