Change SELinux File Contexts Safely with chcon

chcon changes an SELinux file context directly, which is exactly the shortcut that gets a service locked out for weeks. This walks through inspecting a file's SELinux context, copying a context from a known-good file, and changing one context component when that is appropriate. Allow about fifteen minutes, plus time to identify the policy and label that should apply. You need GNU coreutils and an SELinux-enabled system with labelled files. The installed command covered here is GNU coreutils 9.4 from Ubuntu package version 9.4-3ubuntu6.3.

Safety boundary: A security context affects what SELinux policy allows. Do not guess a context, run a broad recursive change, or use sudo merely because the command exists. Work on one test file first and keep a known-good reference available.

1. Confirm the command and the current context

These checks are ordinary read-only commands. They do not need elevated privileges unless the path itself is restricted:

$ command -v chcon
/usr/bin/chcon
$ chcon --version
chcon (GNU coreutils) 9.4
$ ls -Z /path/to/file
unconfined_u:object_r:user_home_t:s0 /path/to/file

The exact ls -Z output depends on your policy. A context commonly has user, role, type and range fields separated by colons. The type is often the field that determines the broad class of access, but do not change it without knowing what the policy expects.

Checkpoint: If ls -Z shows ?, or the command cannot obtain a security context, stop. The local machine used to verify this guide has unlabeled files and reports chcon: failed to get security context ... No data available. That is an environmental limitation, not a reason to force a label.

2. Copy a complete context from a known-good file

The safest useful operation is usually to copy the complete context from a file that serves the same purpose. This avoids manually assembling a four-part value:

$ ls -Z /srv/app/current/index.html /srv/app/previous/index.html
system_u:object_r:httpd_sys_content_t:s0 /srv/app/current/index.html
system_u:object_r:httpd_sys_content_t:s0 /srv/app/previous/index.html
$ sudo chcon --reference=/srv/app/previous/index.html /srv/app/current/index.html
$ ls -Z /srv/app/current/index.html
system_u:object_r:httpd_sys_content_t:s0 /srv/app/current/index.html

Replace both paths with real files from the same policy domain. --reference uses the reference file's security context instead of a CONTEXT argument. The command changes the target file, not the reference file. Elevated privileges are needed only when your account cannot modify the target's metadata.

Keep the reference path exact. A file that merely has a similar name, or one copied from a different service, can carry the wrong label.

3. Change one context component when you have a verified value

Use -u, -r, -t or -l to replace the user, role, type or range component. For example, after confirming that the installed policy expects this type:

$ sudo chcon --type=httpd_sys_content_t /srv/app/current/index.html
$ ls -Z /srv/app/current/index.html
system_u:object_r:httpd_sys_content_t:s0 /srv/app/current/index.html

A partial change preserves the other context components when the file is already labelled. It is not a way to label an arbitrary file with a plausible type. On the unlabelled test file, the installed command rejected --type=user_tmp_t with can't apply partial context to unlabeled file.

For a complete explicit context, use the first syntax shown by the manual:

$ sudo chcon 'system_u:object_r:httpd_sys_content_t:s0' /srv/app/current/index.html
$ ls -Z /srv/app/current/index.html

Quote the context so the shell passes it as one argument. Do not paste a value from an unrelated host without checking its policy and range.

4. Verify the change and its scope

Use --verbose for a diagnostic for every file processed, then inspect the result:

$ sudo chcon --verbose --reference=/srv/app/previous/index.html /srv/app/current/index.html
changing security context of '/srv/app/current/index.html'
$ ls -Z /srv/app/current/index.html
system_u:object_r:httpd_sys_content_t:s0 /srv/app/current/index.html

The wording of the diagnostic can vary, so use the exit status and the resulting context as the main checks. If the command reports an error, do not treat a partially printed diagnostic as success.

By default, symbolic links are dereferenced: the command affects the file they refer to. Use -h or --no-dereference when you deliberately need to affect the link itself. Check the link first:

$ readlink /srv/app/current/index.html
/srv/app/releases/2026-09-22/index.html
$ sudo chcon --no-dereference --reference=/srv/app/previous/index.html /srv/app/current/index.html
$ ls -Zd /srv/app/current/index.html
system_u:object_r:httpd_sys_content_t:s0 /srv/app/current/index.html

Changing a link rather than its referent is specialised work. Confirm that your policy and filesystem support it before using this form.

5. Treat recursive changes as a separate, risky operation

-R applies the operation to a directory tree. It can alter many files and may affect service behaviour immediately. First list the intended tree and choose a narrow test directory. Then use --verbose and preserve the default -P traversal behaviour unless you have a documented reason to follow links:

$ find /srv/app/releases/test -maxdepth 2 -print
$ sudo chcon --recursive --verbose --reference=/srv/app/previous/index.html /srv/app/releases/test
$ ls -ZR /srv/app/releases/test

Do not use -L casually: it follows every symbolic link to a directory encountered during recursion. -H follows a command-line link to a directory. The default -P does not traverse symbolic links. --preserve-root makes recursive operation on / fail; use it as an extra guard in scripts rather than relying on a carefully typed path.

Warning: There is no generic undo value. Before changing a tree, record its contexts or use a known-good reference strategy. To undo a single change, run chcon --reference with the original, verified file as the reference. If the intended labels come from policy rules, use your distribution's documented relabelling workflow instead of inventing replacements.

Common failure messages

Done means