SkillAgentSearch skills...

Bitrix.infra

https://favor-group.ru Bitrix site infrastructure as a code

Install / Use

/learn @paskal/Bitrix.infra
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Bitrix infrastructure as a code Build Status PHP Build Status Pull Status

This repository contains infrastructure code behind Bitrix-based site of my father's metal decking business operating in multiple cities.

It's a Bitrix website completely enclosed within docker-compose to be as portable and maintainable as possible, and a set of scripts around its maintenance like dev site redeploy or production site backup.

Architecture

flowchart TB
    User["Browser"] -->|"HTTP/3, TLS 1.3,<br>Brotli"| Nginx

    subgraph Docker["Docker Compose"]
        Nginx["Nginx<br>(brotli + lua + HTTP/3)"]

        Nginx -->|"FastCGI :9000"| PHP["PHP-FPM 8.4"]
        Nginx -->|"static files"| Web["Web Files<br>prod / dev"]

        PHP -->|"Unix socket"| MySQL[("Percona MySQL 8.0<br>(socket-only, no TCP)")]
        PHP --> Memcached["Memcached<br>Cache (2 GB)"]
        PHP --> MemSessions["Memcached<br>Sessions (128 MB)"]
        PHP --> Web

        PHPCron["PHP Cron<br>(agents, exports,<br>sitemaps)"] -->|"Unix socket"| MySQL
        PHPCron --> Memcached
        PHPCron --> MemSessions
        PHPCron --> Web

        subgraph Optional["Optional Services (profiles)"]
            Certbot["DNSroboCert<br>(Let's Encrypt)"]
            Zabbix["Zabbix Agent 2"]
            Adminer["Adminer"]
            Updater["Updater<br>(webhooks)"]
            FTP["Pure-FTPD"]
        end

        Zabbix -->|"monitor"| MySQL
        Zabbix -->|"monitor"| Nginx
        Adminer -->|"Unix socket"| MySQL
    end

    subgraph HostCron["Host Cron"]
        Backup["Backups<br>(duplicity + mysqldump)"]
        Minify["JS/CSS Minify<br>(hourly)"]
        ImgOpt["Image Optimisation<br>(weekly)"]
    end

    Backup -->|"incremental + dumps"| S3[("Yandex S3")]
    Certbot -->|"DNS-01 challenge"| YcDNS["Yandex Cloud DNS"]

    subgraph Regions["Domains"]
        MSK["favor-group.ru"]
        SPB["spb.favor-group.ru"]
        Tula["tula.favor-group.ru"]
        Dev["dev.favor-group.ru"]
        CDN["static.cdn-favor-group.ru"]
    end

    Regions --> Nginx

The site serves three regions (Moscow, St Petersburg, Tula) via subdomains, each with its own robots.txt, sitemap, redirect map, and product export feeds. All traffic goes through a single nginx instance with HTTP/3 (QUIC), brotli compression, and multi-layer bot detection. MySQL is accessible only via Unix socket (no TCP port exposed). Backups run to Yandex Object Storage: incremental file backups via duplicity daily, MySQL dumps twice daily.

Is it fast?

You bet! Here is a performance on Yandex.Cloud server with Intel Cascade Lake 8 vCPUs, 16Gb of RAM and 120Gb SSD 4000 read\write IOPS and 60Mb/s bandwidth.

<img width="1100" alt="image" src="https://user-images.githubusercontent.com/712534/172490266-88710b9f-3776-4c5b-9852-590181d1d204.png">

What's inside?

Core

Multi-region setup

The site serves three cities — Moscow (favor-group.ru), Saint Petersburg (spb.favor-group.ru) and Tula (tula.favor-group.ru) — from a single Bitrix installation, database and document root. The Bitrix aspro.max module handles region-aware content, while nginx and cron scripts handle the SEO layer.

<details><summary>How multi-region SEO works</summary>
  • robots.txt — nginx rewrites /robots.txt to /aspro_regions/robots/robots_$host.txt, so each subdomain gets its own file. A cron script (alter-robots-txt.sh, every 10 minutes) patches these files after Bitrix regenerates them: Moscow indexes everything, SPb blocks /info/blog/ (centralised on Moscow to avoid duplicate content), Tula additionally blocks /montag/ and /projects/ which don't exist for that region.
  • sitemaps — nginx rewrites /sitemap*.xml to /aspro_regions/sitemap/sitemap*_$host.xml. Four cron jobs generate them nightly: sitemap.bitrix.php, sitemap.aspro.php, sitemap.offers.php and sitemap.regions.php.
  • redirect mapsconfig/nginx/conf.d/redirects-map.conf contains four map blocks: one per region ($new_uri_msk, $new_uri_spb, $new_uri_tula) for region-specific redirects (e.g. Tula bounces all /montag/ and /projects/ URLs to Moscow), plus a global $new_uri map for site-wide URL cleanup.
</details>

Yandex Metrika cookie extension

Safari's Intelligent Tracking Prevention (ITP) limits cookies set by JavaScript to 7 days (24 hours in some cases). This means the Metrika visitor identifier (_ym_uid) expires between visits, causing returning visitors to appear as new ones in analytics. Following Yandex's official recommendation, nginx re-sets the Metrika cookies (_ym_uid, _ym_d, _ym_ucs) server-side via Set-Cookie headers with a 1-year lifetime — browsers respect the full expiry for server-set cookies.

<details><summary>Implementation details</summary>

The implementation uses nginx map blocks (config/nginx/conf.d/metrika-cookies.conf) rather than if directives to avoid the "if is evil" problem — using add_header inside an if block replaces all parent-level headers, which would drop Cache-Control, security headers and CSP from static file responses. When the cookie is absent the map resolves to an empty string and no header is emitted.

</details>

Optional

  • PHP cron container (php-cron) with same settings as PHP serving web requests
  • adminer (adminer) as phpMyAdmin alternative for work with MySQL
  • pure-ftpd (ftp) for FTP access
  • DNSroboCert (certbot) for Let's Encrypt HTTPS certificate generation
  • zabbix-agent2 (zabbix-agent, ghcr.io/paskal/zabbix-agent2) for monitoring
  • Webhooks server (updater) for automated tasks.

Automation (host cron)

These run on the host machine outside Docker, scheduled via config/cron/host.cron:

  • JS/CSS minification — runs hourly via tdewolff/minify Docker image on web/prod/local and web/dev/local, producing .min.js/.min.css files
  • Image optimisation — runs weekly (Saturday night) via scripts/optimise-images.sh, processing PNG (optipng + advpng), JPEG (jpegoptim), WebP (cwebp) and GIF (gifsicle) in web/prod/upload. Uses a SQLite database to track already-processed files and avoid redundant work
  • Log rotation — configured in config/logrotate/ for nginx (weekly for production access logs at 100 MB minimum, monthly for others) and PHP (monthly for error, cron and msmtp logs). Nginx logs are reopened via nginx -s reopen, PHP-FPM via USR1 signal

Bitrix configuration

These are the relevant Bitrix config files that connect the CMS to the dockerised services (memcached for sessions/cache, MySQL via socket, cron agents). Documentation: sessions 1 2 (ru 1, 2), cache (ru)

<details><summary>bitrix/php_interface/dbconn.php</summary>
// Enable cron-based agent execution
define('BX_CRONTAB_SUPPORT', true);

// Database connection (legacy, also configured in .settings.php)
$DBType = "mysql";
$DBHost = "localhost";
$DBName = "<DBNAME>";
$DBLogin = "<DBUSER>";
$DBPassword = "<DBPASSWORD>";

// Temporary files directory
define('BX_TEMPORARY_FILES_DIRECTORY', '/tmp');

// Standard Bitrix configuration
define("BX_UTF", true);
define("BX_FILE_PERMISSIONS", 0644);
define("BX_DIR_PERMISSIONS", 0755);
@umask(~(BX_FILE_PERMISSIONS|BX_DIR_PERMISSIONS)&0777);
define("BX_DISABLE_INDEX_PAGE", true);
</details> <details><summary>bitrix/.settings.php</summary>
  'session' => array (
  'value' =>
  array (
    'mode' => 'separated',
    'lifetime' => 14400,
    'handlers' =>
    array (
      'kernel'  => 'encrypted_cookies',
      'general' =>
      array (
        'type' => 'memcache',
        'host' => 'memcached-sessions',
        'port' => '11211',
   
View on GitHub
GitHub Stars89
CategoryDevelopment
Updated4d ago
Forks30

Languages

Shell

Security Score

100/100

Audited on Mar 27, 2026

No findings