You spin up a fresh Ubuntu or Debian VPS, SSH in, and the very first command you run — apt update, sudo anything, even just logging in — spits out a wall of yellow warnings like perl: warning: Setting locale failed. or locale: Cannot set LC_ALL to default locale: No such file or directory. Nothing's actually broken yet, but it's noisy, it clutters every script's output, and if you leave it alone it eventually breaks something real — Certbot renewals, MySQL imports, even Git commits with non-ASCII author names. Here's what's actually going on and how to fix it for good.
Symptom: What You're Actually Seeing
The exact wording varies slightly by distro, but it's always one of these:
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
LANGUAGE = "en_US:en",
LC_ALL = (unset),
LC_CTYPE = "UTF-8",
LANG = "en_US.UTF-8"
are supported and installed on your system.
perl: warning: Falling back to a fallback locale ("en_US.UTF-8").
Or, from a shell built-in rather than Perl:
locale: Cannot set LC_ALL to default locale: No such file or directory
It shows up whenever a program calls setlocale() and the locale your environment claims to be using isn't actually generated on the box. Perl is just the most talkative offender — it prints this warning on almost every invocation, and a lot of system tools (including dpkg and some Certbot hooks) shell out to Perl internally, so you see it constantly even if you never touch Perl yourself.
Cause: A Locale Is Declared but Never Generated
Most VPS base images are built from a minimal cloud template that ships with only the C and C.UTF-8 locales actually generated on disk. But your SSH client, your terminal emulator, or a provisioning script sets environment variables like LANG=en_US.UTF-8 or LC_ALL=en_US.UTF-8 anyway — usually inherited from your local machine over SSH (check ~/.ssh/config or your terminal's "send locale" setting), or set by a cloud-init template that assumes the full locale package is installed.
Every program that respects locale settings — glibc, Perl, Python, MySQL client tools — tries to load that locale's data files at startup. If en_US.UTF-8 was never generated with locale-gen, the load fails, and depending on the program you either get a loud warning (Perl) or silent fallback to the C locale (most other tools, which is why you might not notice until something depends on proper UTF-8 collation).
Run this to see the actual mismatch:
locale
You'll typically see something like this on an affected box:
| Variable | What you'll see |
|---|---|
| LANG | en_US.UTF-8 |
| LC_ALL | (unset) |
| LC_CTYPE | "UTF-8" (invalid, no country code) |
Then confirm which locales are actually installed:
locale -a
If that list only shows C, C.UTF-8, and POSIX — but not en_US.utf8 — you've found the gap. The system is being told to use a locale it never generated.
Fix: Generate the Locale and Set It Properly
The fix is the same shape on any Debian-based system (Ubuntu, Debian) and only slightly different on RHEL-family systems (AlmaLinux, Rocky). Pick your distro below.
On Ubuntu / Debian
Install the locales package if it's missing, uncomment the locale you want in /etc/locale.gen, and regenerate:
sudo apt update
sudo apt install -y locales
sudo sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen
sudo locale-gen
sudo update-locale LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8
Then either log out and back in, or run source /etc/default/locale in your current shell to pick up the change without a fresh SSH session.
On AlmaLinux / Rocky Linux / CentOS
RHEL-family systems usually ship with more locales already generated, but if glibc-langpack-en is missing you'll see the same warning:
sudo dnf install -y glibc-langpack-en
sudo localectl set-locale LANG=en_US.UTF-8
localectl writes the setting to /etc/locale.conf, which takes effect on the next login.
Verify the Fix
locale
Every line should now show en_US.UTF-8 (or whichever locale you generated) with no blank or "unset" values, and no warning should print the next time you run apt update or any Perl-based tool.
If You Don't Care About a Specific Language, Just Force C.UTF-8
If you don't actually need US English formatting — you just want UTF-8 support without the warning — the simplest fix on any distro is to standardize on the locale that's basically guaranteed to exist everywhere:
export LC_ALL=C.UTF-8
export LANG=C.UTF-8
Add those two lines to /etc/environment (system-wide, no per-user file needed) to make it permanent:
echo 'LC_ALL=C.UTF-8' | sudo tee -a /etc/environment
echo 'LANG=C.UTF-8' | sudo tee -a /etc/environment
This is a good default for automation-only servers where nothing needs region-specific date/currency formatting — it sidesteps the whole "did I remember to generate the locale" problem because C.UTF-8 ships built-in on virtually every modern distro.
Why This Also Matters Beyond the Warning Noise
Left unfixed, a broken locale isn't just cosmetic. A few places it actually bites:
- MySQL/MariaDB imports can fail or silently mis-sort text columns if the client's locale doesn't match what the dump expects, especially with multi-byte characters.
- Certbot and other Python-based tools sometimes throw a hard error (
UnicodeDecodeErrororValueError: unknown locale) instead of a warning, particularly during cron-triggered renewals where the environment is even more stripped down than an interactive shell. - Git commit messages with accented characters or emoji can get mangled on push/pull if the encoding assumptions don't match.
- Cron jobs run with a minimal environment by default, so a script that works fine interactively can throw locale errors only when it runs unattended — worth checking if a cron job's error emails start showing this warning even though manual runs look clean.
If you manage a fleet of VPS instances, it's worth baking the locale fix into your provisioning script or cloud-init template once, rather than fixing it by hand on every new box.
Prevention: Bake It Into Provisioning
If you're scripting VPS setup (cloud-init, Ansible, a plain bash bootstrap), add the locale generation as one of the first steps, before you install anything that shells out to Perl or Python:
# Debian/Ubuntu cloud-init snippet
runcmd:
- locale-gen en_US.UTF-8
- update-locale LANG=en_US.UTF-8
And if you're connecting from a Mac or a Linux desktop over SSH, check whether your client is forwarding locale environment variables it shouldn't be. On macOS/Linux SSH clients, look for SendEnv LANG LC_* in ~/.ssh/config — if your local machine uses a locale the remote server doesn't have, removing that line stops the client from pushing a mismatched value on every connection.
Frequently Asked Questions
Is this warning actually harmful, or can I just ignore it?
By itself, the warning is harmless — Perl falls back to a working locale and keeps going. The risk is everything downstream that silently behaves differently under the fallback (sorting order, date formatting, encoding assumptions), plus tools like Certbot or MySQL that throw hard errors instead of just warning. It's a five-minute fix, so there's little reason to leave it.
Why does this only show up on some fresh VPS instances and not others?
It depends on the base image and how you connect. Minimal cloud images (especially Ubuntu's official cloud templates) often ship without full locale data to save space. Whether you notice the warning also depends on your SSH client forwarding a specific LANG/LC_ALL value — two people connecting to the identical fresh VPS can get different results based on their own terminal's locale settings.
I ran locale-gen but the warning is still there. What am I missing?
Check that you regenerated the exact locale string your environment is requesting — locale shows the value with an underscore and hyphen (en_US.UTF-8), but locale -a often lists it without the hyphen (en_US.utf8); that's normal and not the bug. More commonly, the fix didn't take because you're still in the old SSH session — log out and reconnect, or run source /etc/default/locale, since environment variables set at login don't refresh mid-session.
Does this affect cPanel or WHM servers the same way?
Yes — cPanel runs on top of the same underlying OS locale settings. It's less commonly hit because cPanel's supported OS images (AlmaLinux, CloudLinux) typically install glibc-langpack-en by default, but if you've stripped down a minimal template before installing cPanel, or you're seeing the warning specifically in WHM's background job logs, the same dnf install glibc-langpack-en fix applies.
Should I use en_US.UTF-8 or C.UTF-8?
Use en_US.UTF-8 (or your actual region's locale) if any application on the server does region-aware formatting — currency symbols, date order, sort order for a user-facing site. Use C.UTF-8 if the server is purely backend automation with no locale-sensitive output; it needs no generation step and behaves identically on every Linux distro, which makes fleet management simpler.
