Fix PHP Sury Repository Errors on Debian 12
Fix the Sury PHP repository error on Debian 12 by correcting NTP, refreshing APT, and safely upgrading installed PHP packages. Follow these steps.

Fix the PHP Sury Repository Error on Debian 12
A failed apt update can make a PHP repository look broken when the real culprit is the server clock. On Debian 12, one common error is:
Release file for https://packages.sury.org/php/dists/bookworm/InRelease is not valid yet
It's tempting to assume the Sury repository is down, then start changing keys or repository settings. Those detours can waste time - and sometimes create new problems.
APT validates repository metadata against your system time. If your clock is behind when that repository release becomes valid, APT can reject the file even if the repository itself is fine. So the first thing to check isn't keys or configuration - it's time sync.
This guide walks through a practical recovery on Debian 12 (Bookworm): diagnose the clock, enable an appropriate NTP service, refresh APT, verify the Sury source, and upgrade only the PHP packages you already have installed. It also includes verification steps and what to look at if the error keeps coming back.
Why APT says the release file is not valid yet
The clue is not valid yet. APT fetched (or tried to fetch) repository metadata from InRelease, but the metadata's validity window doesn't match your local system clock.
If your clock is behind - often by more than you'd expect - that mismatch is a common cause. Other things can contribute too: a stale proxy/cache, incorrect repository config, or a wrong suite. Still, the clock is the first thing to test because it affects everything - not just this one repository.
Start with:
date
date -u
timedatectl status
date shows local time; date -u shows UTC. In timedatectl status, look for lines like:
System clock synchronized: yes
NTP service: active
Exact wording can vary with systemd and the time service in use. Also, "the timezone looks right" doesn't guarantee the clock is correct, and it doesn't guarantee you're actually synchronized. Compare UTC against a trusted reference and check the reported sync state.
This matters because APT isn't just being picky - it's doing part of its security validation. Repository metadata includes validity timing, and accurate time also supports TLS certificate checks and makes logs and scheduled tasks line up correctly. Skipping the checks might get one update through, but it doesn't solve the real timing problem.
If the clock is correct and synchronized and the error remains, then broaden the investigation. Confirm the repository URL/suite, check for a proxy or caching layer serving old metadata, and compare what APT is seeing with the repository's current state. Also, don't change five things at once - otherwise you won't know what actually fixed it.
Correct and synchronize the Debian system clock
Debian 12 often uses systemd-timesyncd, but not every server has it installed or running. Some systems use chrony (or something else). Pick one time-synchronization service and let it do the job. Multiple clients fighting over the same clock leads to confusing results.
Check the existing time service
First, see whether systemd-timesyncd is installed and active:
systemctl status systemd-timesyncd
If it's present and appropriate, enable NTP syncing:
sudo timedatectl set-ntp true
timedatectl status
timedatectl timesync-status
timedatectl timesync-status is useful when systemd-timesyncd is in charge. If it's missing or you don't see synchronization, focus on service status and network reachability instead of repeatedly toggling settings.
If you're using chrony, check that instead - for example:
systemctl status chrony
chronyc tracking
And if this is cloud or managed infrastructure, it's worth checking whether the provider already supplies (or manages) a time source. Don't blindly enable systemd-timesyncd as a second client.
Configure NTP servers when needed
If you need to specify NTP servers for systemd-timesyncd, edit its config:
sudo nano /etc/systemd/timesyncd.conf
Set the [Time] section like this (adjust for your environment):
[Time]
NTP=0.pool.ntp.org 1.pool.ntp.org 2.pool.ntp.org
FallbackNTP=0.debian.pool.ntp.org 1.debian.pool.ntp.org
On managed production networks, use an organization-approved time source when one is provided. Public pool servers aren't automatically wrong - you just need to match your network policy and provider guidance.
After saving, restart and trigger syncing:
sudo systemctl restart systemd-timesyncd
sudo timedatectl set-ntp true
timedatectl status
timedatectl timesync-status
Large clock corrections won't always show up immediately. Confirm the service can reach its NTP server(s) and give it time to synchronize before moving on.
If APT is currently unusable and you don't have a working time sync service, follow your infrastructure's supported recovery process first. Then fix time and get APT working. Try not to "guess" a production server's time by hand unless you're doing a controlled recovery.
Refresh APT and check the Sury repository
Once time is synchronized, refresh APT:
sudo apt update
Read the output. If the clock was the problem, the Release file ... is not valid yet message should disappear.
If it still shows up, don't jump straight to disabling date checks. Re-check UTC time and NTP status, then look for another cause - especially whether your system can reach the repository and whether a proxy/cache is serving outdated metadata.
Confirm the source points to Bookworm
Sury's PHP repository is a third-party repository (not Debian's official repository). If it's already configured correctly, you probably don't need to add it again.
Check what's in:
/etc/apt/sources.list.d/
Then confirm the suite matches Debian 12's codename, bookworm.
If the Sury repo isn't configured, make sure APT can reach Debian's existing repositories. If needed, install the basics:
sudo apt install -y ca-certificates curl lsb-release
Install the Sury archive keyring using the repository's published instructions:
sudo curl -fsSL \
https://packages.sury.org/debsuryorg-archive-keyring.deb \
-o /tmp/debsuryorg-archive-keyring.deb
sudo apt install /tmp/debsuryorg-archive-keyring.deb
Configure the source with the matching keyring and the Debian codename:
sudo tee /etc/apt/sources.list.d/php.sources > /dev/null <<EOF
Types: deb
URIs: https://packages.sury.org/php/
Suites: $(. /etc/os-release && echo "$VERSION_CODENAME")
Components: main
Signed-By: /usr/share/keyrings/debsuryorg-archive-keyring.gpg
EOF
On Debian 12, that should resolve to bookworm. If you'd rather avoid shell substitution, verify the codename first and set it explicitly. Don't switch to a different Debian suite just to make the error go away - you'll end up with a different (and usually bigger) problem.
Refresh indexes and inspect what APT sees:
sudo apt update
apt-cache policy php
apt list --upgradable 2>/dev/null | grep -i php
apt-cache policy shows candidate versions and their sources. The upgradable list is helpful, but an empty result isn't a full security audit - it just means there's nothing matching upgrades for that APT query right now.
For deeper context on time validation and repository trust, see:
Upgrade and verify installed PHP packages
Before upgrading, find out what's actually installed. Many servers have multiple PHP components, and the CLI version (php -v) isn't automatically the one your web app uses.
dpkg-query -W \
-f='${binary:Package}\t${Version}\n' \
'php*' 2>/dev/null
php -v
You'll commonly see packages like php8.2-cli, php8.2-common, php8.2-fpm, and extensions such as php8.2-mysql, php8.2-xml, php8.2-curl, php8.2-mbstring, and more.
php -v tells you the CLI runtime version. It doesn't confirm which PHP-FPM service (or Apache module) your site is using.
Simulate the upgrade before applying it
Be careful with wildcard installs: a pattern like php* might include packages you don't currently have installed. If your goal is "update what's already on the server," build the list from installed packages and simulate first.
Run this in Bash:
mapfile -t PHP_PACKAGES < <(
dpkg-query -W -f='${binary:Package}\n' 'php*' 2>/dev/null |
grep -E '^php([0-9.]|[-:])'
)
printf '%s\n' "${PHP_PACKAGES[@]}"
Check the output. If the list is empty, stop and figure out why - don't run an upgrade command with no package names.
Then simulate:
sudo apt-get -s install --only-upgrade "${PHP_PACKAGES[@]}"
Review what APT wants to change. Confirm it's not removing something important, switching to an unexpected PHP version, or making extra changes you didn't plan. This simulation isn't perfect, but it's a low-effort way to catch surprises before you apply them.
If the plan looks right, apply it:
sudo apt-get install --only-upgrade "${PHP_PACKAGES[@]}"
--only-upgrade limits the requested operation to packages that are already installed. APT may still adjust dependencies to complete a consistent upgrade - so the earlier review still matters. This approach also avoids "accidentally installing every PHP version available from Sury."
Verify the runtime your application uses
After the upgrade, re-check:
php -v
php --ini
apt list --upgradable 2>/dev/null | grep -i php
If you use PHP-FPM, look at the services and check the one your app relies on:
systemctl list-units --type=service --all 'php*-fpm.service'
systemctl status php8.2-fpm
Replace php8.2-fpm with the actual service name on your server.
If you run Apache or Nginx, verify that the web server is wired to the PHP runtime you just upgraded. Then test a representative URL, scheduled tasks, extensions used by the app, and any deployment checks that depend on PHP. A CLI command succeeding doesn't prove PHP-FPM restarted cleanly or that the site is actually using the upgraded version.
Check security status, not just version numbers
A successful upgrade doesn't automatically mean every relevant vulnerability is fixed. Check the candidate and installed versions for the packages you care about:
apt-cache policy php8.2-cli
apt-cache policy php8.2-fpm
Use the actual package names from your system. Review Debian and Sury advisories and the relevant package changelogs. Don't rely on the major/minor PHP version alone - distribution packages can include security fixes without matching the upstream PHP release string exactly.
Also check your application's compatibility requirements if you're moving across PHP branches. For production systems, use your normal maintenance window (and deployment process), keep a record of what APT planned to install, test after the update, and monitor PHP-FPM and web-server logs. If something breaks after upgrading, diagnose the real failure - don't assume it's just "the repository clock again."
Conclusion: The sequence that usually works is simple: verify and synchronize time, run apt update, confirm the Sury repository configuration, simulate the PHP upgrade, then apply and verify the changes. Correct time is basic infrastructure - not a workaround. If you manage Debian servers, add clock synchronization and post-upgrade runtime checks to your routine.