Skip to content

Version 4.0.0

Choose a tag to compare

@paskal paskal released this 14 Oct 04:42
· 27 commits to master since this release
0a45299

php and php-cron move to PHP 8.5, with sessions kept in memcached through PHP's native handler. Disaster recovery can restore from another hostname and S3 endpoint, rebuild nested Git checkouts and run site-specific steps, backups can go to a second S3 destination, and the deploy account is configurable. Read the upgrade notes before pulling.

Why a major version

docker-compose.yml now runs php and php-cron on the bitrix-php:8.5 image with their config binds under /etc/php/8.5. That image does not ship the memcache extension, so a Bitrix site whose bitrix/.settings.php uses the memcache session type fails every request after the switch. Bitrix has no memcached session type; the save_handler.php.ini type hands sessions to PHP's native handler, which config/php/90-php.ini now points at memcached-sessions:11211. Change the session setting before pulling (upgrade note 1).

PHP and sessions

  • Sessions use the php-memcached native handler. A request waits up to about 30 seconds for a session lock held by another request of the same visitor, and a lock expires after 60 seconds.
  • nginx resolves the php upstream once, so after php is recreated it keeps the old container address and answers 502 until it reloads; the PHP switching steps in the Readme say so.

MySQL and memcached

  • sort_buffer_size increases from 256K to 2M to reduce disk-based sorts. MySQL grows sort memory as needed, so this is a per-connection cap, not a reservation.
  • The MySQL container sets oom_score_adj: -500 to make it less likely to be selected during an out-of-memory event.
  • The component cache memcached gets 3 GB instead of 2 GB.
  • Sizing: the 4 GB InnoDB buffer pool (unchanged since v3.0.0) needs a host with at least 8 GB of RAM; on a 4 GB host the kernel kills mysqld while it initialises the data directory. The Readme says how to shrink it locally.

nginx

  • The open file cache is off. It kept a replaced file's descriptor and size for up to 30 seconds, so a new versioned URL could receive the old bytes, or the new content cut to the old size, and browsers could keep that response for the configured cache lifetime.
  • In vhosts using bitrix.conf, the root icon paths (/favicon.ico, /favicon.svg, /apple-touch-icon.png) are cached for a day instead of as immutable, and feed .xml and .txt files under /upload/ are revalidated within the hour.
  • The static-host CORS origin is an overlay map in conf.d/overlay-maps.conf, so including static-cdn.conf without a private overlay no longer fails the configuration test.

Disaster recovery

  • RESTORE_FROM_HOST selects the hostname whose backups are restored, and RESTORE_S3_ENDPOINT and RESTORE_AWS_PROFILE select the S3 endpoint and the credentials profile. The bucket, domain and database name remain site-specific values at the top of the script. Without the variables the script restores from the default hostname and endpoint as before; the credentials now come from the deploy account's ~/.aws.
  • After the restore merge, the repository's tracked files are put back to the clone, since the backup's copies can be older. DR_NESTED_REPOS lists restored checkouts, such as the private overlay, as <dir>=<git url> pairs; the script turns each into a Git repository tracking origin/master that keeps the restored working files, using the restored private/updater_ssh_key and a pinned GitHub host key, because the backups leave out .git.
  • An executable private/scripts/dr-hook.sh is called with pre-start before the containers start and with post-db after the database import, for site-specific steps.
  • The script installs the host tools the backup jobs need, sets up the deploy account (uid 1000, passwordless sudo, Docker group, the updater's SSH key) and works without a terminal; the Readme section "Disaster recovery" has the details.
  • The database import waits until the final mysqld is running and accepts the root login. On a fresh host the image's temporary initialisation server already answered mysqladmin ping, so the import started too early and failed with ERROR 1045.
  • The MTU settings for Yandex Cloud DDoS protection apply only on Yandex Cloud; on other hosts the network is left alone.

Backups

  • When BACKUP2_S3_BUCKET is set in backup.env, file-backup.sh also runs its own duplicity chain against that bucket and mysql-dump.sh syncs the dumps there. A failure at one destination does not stop the other; the run still exits non-zero and leaves the monitoring status file unwritten.

Deploy account

  • scripts/deploy-user.sh resolves the account that owns the checkouts, runs the deploy pulls and keeps the backup credentials in ~/.aws, from DEPLOY_USER in the environment or in .env, with admin as the default. The recovery, permission, pull and backup scripts use it, and the DNS token cron job runs as it through runuser.
  • When that account has uid 1000, the nightly fix-rights.sh also gives the tracked files, every directory recorded in HEAD and .git of the public and private checkouts to it, sets private/environment secrets to mode 0600 and the Zabbix agent's MySQL credentials to uid 1997 with mode 0400. Cron and logrotate files stay root-owned and MySQL configuration stays uid 1001. The repair is skipped when the account does not have uid 1000.

Dev renewal

  • renew-dev.sh reads its site-specific values from the environment, with the previous values as defaults, and refuses destinations that overlap production. It now needs GNU realpath -m, refuses a production dbconn.php whose $DBLogin or $DBName is not a single quoted literal assignment, and no longer rewrites the skypark.cdn module's cdn_domains1; provide that through POST_RENEW_HOOK if you need it.

Other changes

  • update-dns-token.sh leaves AUTH_KEY alone when the DNS provider is not Yandex Cloud.
  • Upload deduplication skips hidden .*-backups rollback directories.
  • The PHPStan triggers carry a notify tag from the template macro {$PHPSTAN.NOTIFY}, empty by default, so alert actions can route them. Re-import the template and set the macro to the value your actions match.

Upgrade notes

  1. Before pulling, change the general session handler in the session block of bitrix/.settings.php to 'general' => array ('type' => 'save_handler.php.ini') (the Readme has the full example). Move any local override that binds files under /etc/php/8.4/ to /etc/php/8.5/. After the pull, with your overlay's COMPOSE_PROFILES set: docker compose pull php php-cron && docker compose up -d php php-cron, then test nginx and reload it (docker compose exec nginx nginx -t && docker compose exec nginx nginx -s reload).
  2. docker compose up -d --timeout 120 mysql recreates mysqld with the new my.cnf and oom_score_adj; it interrupts the database.
  3. Recreate memcached with docker compose up -d memcached for the 3 GB limit, and check the host has the memory.
  4. nginx reads nginx.conf through a file-level bind mount: test the new file in a fresh container as the Readme describes, then restart nginx.
  5. If the host has an account named admin with uid 1000, the next fix-rights.sh run changes the ownership of both checkouts and of private/environment as described above. If your deploy account has another name, set DEPLOY_USER in .env before that run, and make sure that account holds the backup credentials in its ~/.aws and, for Yandex Cloud DNS renewal, the yc CLI in its home.