Setting up CI/CD for a 1C-Bitrix project

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1177
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

CI/CD Configuration for a 1C-Bitrix Project

Most Bitrix projects are deployed using the same approach: FTP/SCP manually or via the hosting file manager. When a team grows to three or more people, this becomes a source of recurring incidents — overwritten changes, untested migrations, and conflicts in bitrix/php_interface/init.php. CI/CD for Bitrix is achievable, but requires accounting for the platform's architectural specifics.

Bitrix Specifics That Affect the Pipeline

Bitrix is not Laravel or Symfony. It has no built-in migration mechanism and no clear boundary between code and data. The main challenges are:

  • Core in /bitrix/ — 500+ MB of files updated via the Bitrix updater, not through git. Storing them in the repository is a bad idea, but deploying without them is impossible.
  • Customizations via /local/ — everything developed by the team must reside here exclusively.
  • No database migrations — structural DB changes are made either via scripts or through the admin interface.
  • bitrix_sessid and cache — after deployment the cache must be cleared, otherwise 500 errors may occur.

Repository Structure

The correct strategy: store only /local/ and root config files (nginx.conf, php.ini patches, .env.example) in git. The Bitrix core stays outside the repository and is synchronized through a separate process.

.git/
local/
  components/
  modules/
  php_interface/
  templates/
upload/           # exclude from git (.gitignore)
bitrix/           # exclude from git (.gitignore)

Minimum .gitignore:

/bitrix/
/upload/
/.env
/bitrix/php_interface/dbconn.php

GitLab CI: Basic Pipeline

# .gitlab-ci.yml
stages:
  - lint
  - test
  - deploy

variables:
  DEPLOY_PATH: /var/www/myshop

php-lint:
  stage: lint
  image: php:8.1-cli
  script:
    - find local/ -name "*.php" -exec php -l {} \; | grep -v "No syntax errors"
  only:
    - merge_requests
    - main

deploy-production:
  stage: deploy
  image: alpine:latest
  before_script:
    - apk add --no-cache openssh-client rsync
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" | ssh-add -
  script:
    - rsync -avz --delete
        --exclude='.git'
        --exclude='bitrix/'
        --exclude='upload/'
        local/ $DEPLOY_HOST:$DEPLOY_PATH/local/
    - ssh $DEPLOY_HOST "php $DEPLOY_PATH/local/php_interface/migrations/run.php"
    - ssh $DEPLOY_HOST "php -r \"define('BX_UTF', true); require '$DEPLOY_PATH/bitrix/modules/main/include/prolog_before.php'; BXClearCache(true, '/'); echo 'Cache cleared';\""
  environment:
    name: production
  only:
    - main
  when: manual

Database Migrations

Bitrix has no built-in migration mechanism, but this can be addressed. A practical approach is a simple custom migrator:

<?php
// local/php_interface/migrations/run.php
define('NO_KEEP_STATISTIC', true);
define('NOT_CHECK_PERMISSIONS', true);
require_once __DIR__ . '/../../../bitrix/modules/main/include/prolog_before.php';

$migrationsDir = __DIR__ . '/sql/';
$appliedFile = __DIR__ . '/.applied_migrations';

$applied = file_exists($appliedFile)
    ? array_filter(explode("\n", file_get_contents($appliedFile)))
    : [];

$files = glob($migrationsDir . '*.sql');
sort($files);

$db = \Bitrix\Main\Application::getConnection();

foreach ($files as $file) {
    $name = basename($file);
    if (in_array($name, $applied)) {
        continue;
    }
    $sql = file_get_contents($file);
    $db->query($sql);
    $applied[] = $name;
    echo "Applied: $name\n";
}

file_put_contents($appliedFile, implode("\n", $applied));

Migrations are named 20250301_120000_add_property_article.sql — chronologically, so the order is deterministic.

Cache Clearing After Deployment

A critical step that is often overlooked:

# Clear all cache via CLI
php -r "
define('BX_UTF', true);
define('NO_KEEP_STATISTIC', true);
\$_SERVER['DOCUMENT_ROOT'] = '/var/www/myshop';
require '/var/www/myshop/bitrix/modules/main/include/prolog_before.php';
BXClearCache(true, '/');
echo 'OK';
"

# Or directly via the cache component
rm -rf /var/www/myshop/bitrix/cache/*
rm -rf /var/www/myshop/bitrix/managed_cache/*

Timeline

Task Duration
Repository setup and .gitignore 0.5 days
Basic GitLab/GitHub Actions pipeline 1 day
DB migration script 0.5 days
Testing and staging validation 1 day