Примерно год я мучался с GitLab на сервере с двумя гигабайтами оперативки. Когда оплаченный период закончился, решил взять более мощный VDS по формуле 4/4/30. До этого сам GitLab был установлен непосредственно из репозитория, но для экспериментов с Pages и т.д. нужен Docker. А раз он и так есть, почему бы не завернуть GitLab в контейнер? Заодно на сервер можно будет установить что-нибудь еще.

Так как Arch Linux мой хостер не предустанавливает, а Rocky для Докера мне представляется избыточным (да и ядра там не слишком новые), в качестве ОС я выбрал Debian 12.

Подготовка

Поскольку на VDS/VPS предоставляется root-доступ, я бессовестным образом под ним и работаю (если явно не указано иное).

Файл подкачки

Наверное надо бы создать на всякий случай. Стандарт на 2 гига:

fallocate -l 2G /swapfile
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile

В /etc/fstab добавляем:

/swapfile none swap defaults 0 0

В /etc/sysctl.conf добавляем:

vm.swappiness=10

Перезагружаемся для пущей важности.

Docker

Практически стандартная установка, разве что можно пропустить первые шаги – ca-certificates, curl и /etc/apt/keyrings по идее есть.

curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  tee /etc/apt/sources.list.d/docker.list > /dev/null
apt update
apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Сервис включается и запускается сразу.

Traefik

Внезапно? wink Поскольку мне не хотелось, чтобы столь мощный (для меня, во всяком случае) сервер ограничивался одним лишь GitLab'ом, я решил поставить данный прокси. Плюсом он возьмет на себя все вопросы с SSL-сертификатами. Да, в прошлый раз я исхитрился применить Let's Encrypt, скажем так, не только для GitLab, но повторять этот опыт мне не хочется совершенно, равно как и лишний раз связываться с Nginx.

По аналогии с моим обзором запустим Traefik под пользователем traefik, изолируем сокет с помощью wollomatic/socket-proxy и защитим панель управления базовой аутентификацией. Разве что пути будут короче, в данном случае /srv/traefik:

mkdir -p /srv/traefik/config
cd /srv/traefik
useradd -c 'Traefik user' -M -s /usr/sbin/nologin traefik
apt install apache2-utils
htpasswd -cbBC 10 config/usersfile admin "P@ssw0rd"

Понятно, что на самом деле пользователь панели не admin и пароль не P@ssw0rd smile

Настройки по сути перекочевали из обзора, разве что я объявил websecure точкой входа по умолчанию, чтобы каждый раз не указывать ее явно – файл /srv/traefik/config/traefik.yaml:

providers:
  docker:
    exposedByDefault: false
    endpoint: 'tcp://dockerproxy:2375'
    network: traefik

api: {}

entryPoints:
  web:
    address: ":10080" # will be routed to port 80
    http:
      redirections:
        entrypoint:
          to: ":443"
          scheme: https
  websecure:
    address: ":10443" # will be routed to port 443
    asDefault: true
    http:
      tls:
        certResolver: letsencrypt
    http3:
      advertisedPort: 443

certificatesResolvers:
  letsencrypt:
    acme:
      email: admin@example.com
      storage: /etc/traefik/acme.json
      tlsChallenge: {}

compose.yaml в немалой степени тоже:

services:
  dockerproxy:
    image: wollomatic/socket-proxy:1
    restart: unless-stopped
    user: "65534:${DOCKERGID:-994}"
    mem_limit: 64M
    read_only: true
    cap_drop:
      - ALL
    security_opt:
      - no-new-privileges:true
    command:
      - '-loglevel=error'
      - '-listenip=0.0.0.0'
      - '-allowfrom=traefik' # allow only hostname "traefik" to connect
      - '-allowGET=/v1\..{1,2}/(version|containers/.*|events.*)'
      - '-allowbindmountfrom=/var/log,/tmp' # restrict bind mounts to specific directories
      - '-watchdoginterval=3600' # check once per hour for socket availability
      - '-stoponwatchdog' # halt program on error and let compose restart it
      - '-shutdowngracetime=5' # wait 5 seconds before shutting down
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    networks:
      - docker-proxynet    # NEVER EVER expose this to the public internet!
                           # this is a private network only for traefik and socket-proxy

  traefik:
    image: traefik:v3.4
    restart: unless-stopped
    read_only: true
    depends_on:
      - dockerproxy
    security_opt:
      - no-new-privileges:true
    ports:
      - "80:10080"  # use high ports inside the container so
      - "443:10443" # we don't need to be root to bind the ports
    networks:
      - default
      - docker-proxynet
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.dashboard.rule=Host(`traefik.example.com`) && (PathPrefix(`/api`) || PathPrefix(`/dashboard`))"
      - "traefik.http.routers.dashboard.service=api@internal"
      - "traefik.http.routers.dashboard.middlewares=apiauth,compress"
      - "traefik.http.middlewares.apiauth.basicauth.usersfile=/etc/traefik/usersfile"
      - "traefik.http.middlewares.compress.compress.encodings=zstd,br,gzip"
      - "traefik.http.middlewares.compress.compress.excludedContentTypes=image/png,image/jpeg,image/gif,font/*"
    volumes:
      - ./config/traefik.yaml:/etc/traefik/traefik.yaml:ro  # static configuration
      - ./config/usersfile:/etc/traefik/usersfile:ro        # dashboard basic auth
      - ./config/acme.json:/etc/traefik/acme.json           # certificate storage
    user: "${TRAEFIKUID:-1000}"

networks:
  default:
    name: traefik
  docker-proxynet:
    internal: true

Отличие в том, что я еще объявил промежуточный слой сжатия данных с приоритетом zstd и исключением сжатия картинок и шрифтов (жаль, что это приходится указывать самим). Не путать идентификатор (первый compress) с типом (второй)! acute

Перед запуском не забудьте уточнить UID (id -u traefik) и GID (getent group docker | cut -d: -f3), а также еще немного похимичить:

touch /srv/traefik/config/acme.json
chmod 600 /srv/traefik/config/acme.json
chown -R traefik:traefik /srv/traefik/config

Вроде бы можно запускать.

GitLab

https://docs.gitlab.com/administration/backup_restore/migrate_to_new_server/

Сохранение данных

В принципе у меня как бы и так все сохранено, разве что Redis вызывает определенные вопросы. В инструкции прямо какая-то сложная последовательность, но на всякий случай наверное ее лучше выполнить.

Итак, отключаем CI/CD – можно подумать, у меня они есть. В /etc/gitlab/gitlab.rb правим:

nginx['custom_gitlab_server_config'] = "location = /api/v4/jobs/request {\n    deny all;\n    return 503;\n  }\n"

И переконфигурирем:

gitlab-ctl reconfigure

Заходим в GitLab под root, админ-панель, и начинаем отключать всякие периодические задания (Monitoring – Background jobs). На вкладке cron отключаем все. Теперь в инструкции предлагается убедиться, что все задачи CI/CD завершены (почему-то говорится об Overview – Jobs, но наверное имеются в виду CI/CD – Jobs). У меня они определенно завершены smile. Возвращаемся в Monitoring – Background jobs и ждем, когда показатели «занят» и «в очереди» достигнут 0.

Сбрасываем БД Redis на диск с помощью весьма хитрой команды, а заодно отключаем лишние сервисы:

/opt/gitlab/embedded/bin/redis-cli -s /var/opt/gitlab/redis/redis.socket save && gitlab-ctl stop && gitlab-ctl start postgresql && gitlab-ctl start gitaly

И так бессовестным образом работаю под root, поэтому sudo поубирал. Архивируем:

gitlab-backup create

Наконец, предлагается остановить все сервисы на уровне gitlab.rb – добавить:

alertmanager['enable'] = false
gitlab_exporter['enable'] = false
gitlab_pages['enable'] = false
gitlab_workhorse['enable'] = false
logrotate['enable'] = false
gitlab_rails['incoming_email_enabled'] = false
nginx['enable'] = false
node_exporter['enable'] = false
postgres_exporter['enable'] = false
postgresql['enable'] = false
prometheus['enable'] = false
puma['enable'] = false
redis['enable'] = false
redis_exporter['enable'] = false
registry['enable'] = false
sidekiq['enable'] = false

В инструкции есть grafana['enable'] = false, мой GitLab на это дело ругнулся и я эту строчку убрал.

Переконфигурируем и проверяем статусы:

gitlab-ctl reconfigure
gitlab-ctl status

Вроде как должны были все сервисы остановиться, но у меня nginx с gitaly так и работали. Наверное ничего страшного.

Установка в Docker

https://docs.gitlab.com/install/docker/installation/

mkdir /srv/gitlab
cd /srv/gitlab

Создал .env:

GITLAB_HOME=/srv/gitlab

Честно говоря, не очень понятно, зачем это надо, но пусть будет раз в инструкции так сказано. Не самый предварительный compose.yaml:

services:
  gitlab:
    image: gitlab/gitlab-ce:18.1.1-ce.0
    container_name: gitlab
    restart: always
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        # Add any other gitlab.rb configuration here, each on its own line
        external_url 'https://git.example.com'
        letsencrypt['enable'] = false
        nginx['listen_port'] = 80
        nginx['listen_https'] = false
        gitlab_rails['gitlab_shell_ssh_port'] = 2424
    ports:
      - '2424:22'
    labels:
      - traefik.enable=true
      - traefik.http.routers.gitlab.rule=Host(`git.example.com`)
      - traefik.http.routers.gitlab.service=gitlab
      - traefik.http.routers.gitlab.middlewares=compress
      - traefik.http.services.gitlab.loadbalancer.server.port=80
    volumes:
      - '$GITLAB_HOME/config:/etc/gitlab'
      - '$GITLAB_HOME/logs:/var/log/gitlab'
      - '$GITLAB_HOME/data:/var/opt/gitlab'
    shm_size: '256m'

networks:
  default:
    name: traefik
    external: true

Обратите внимание на явное указание версии GitLab. Поскольку на старом сервере работала версия 18.1.1, точно такая же должна быть и на новом для успешного восстановления из бэкапа.

Укажем наиболее критические настройки в переменной окружения GITLAB_OMNIBUS_CONFIG. Сразу же отключаем Let's Encrypt – этим у нас Traefik занимается. Для этого же настраиваем Nginx на прослушивание 80-го порта без https. Поскольку в контейнере первым открыт 22-й порт, 80-й нужно явно указать для Traefik'а.

Также ставим нестандартный порт SSH для git'а – мне как бы на сам сервер тоже иногда надо попадать. С другой стороны, я вроде как в настройках в принципе только HTTP(S) оставил, так что можно было бы наверное и вовсе не пробрасывать никакой порт. С третьей стороны, SSH можно было бы пробросить через Traefik по имени хоста как TCP-сервис.

Где-то ±в этот момент надо бы перенацелить DNS.

В первый раз предлагаю запустить просто docker compose up – нам же потом это все надо будет останавливать (Ctrl+C в данном случае), а заодно можно и за процессом установки понаблюдать (довольно длительным).

Теперь пора восстанавливать конфигурацию. В моем случае это настройки отправки почты, gitlab_rails['omniauth_providers'] – подключение к BitBucket (я в свое время импортировал оттуда репозитории), время хранения бэкапов gitlab_rails['backup_keep_time'] = 604800 и отключение реестра контейнеров registry['enable'] = false.

Также есть некоторые оптимизации по памяти:

puma['worker_processes'] = 0
sidekiq['concurrency'] = 8
prometheus_monitoring['enable'] = false

И настройки Pages:

pages_external_url 'https://pages.example.com'
gitlab_pages['enable'] = true
gitlab_pages['namespace_in_path'] = true
pages_nginx['enable'] = false

Для связки с Traefik как обратного прокси конфигурацию надо доработать. Для работы Pages указываем gitlab_pages['listen_proxy'] = "0.0.0.0:8090" – по умолчанию прослушивается localhost, поэтому извне контейнера изначально доступа нет.

If running GitLab Pages behind a reverse proxy with TLS termination, specify listen_proxy instead of external_http.

Для получения реального IP-адреса Nginx'ом Гитлаба прописываем доверенные прокси (с запасом):

nginx['real_ip_trusted_addresses'] = ['172.16.0.0/12']

А также, раз мы передаем управление сжатием Трафику, отключаем ее в настройках: nginx['gzip_enabled'] = false. Если честно, не особо помогает, поскольку то ли конь (Workhorse), то ли кошка (Puma) сами жмут в gzip и как это отключить, совершенно непонятно. Но хотя бы запросы к graphql сжимаются.

Соответственно добавляем метки в compose.yaml

      - traefik.http.routers.pages.rule=Host(`pages.example.com`)
      - traefik.http.routers.pages.service=pages
      - traefik.http.routers.pages.middlewares=compress
      - traefik.http.services.pages.loadbalancer.server.port=8090

И дополнительно открываем порт:

    expose:
      - '8090'

По идее симметрично старому серверу надо временно отрубить CI/CD:

nginx['custom_gitlab_server_config'] = "location = /api/v4/jobs/request {\n    deny all;\n    return 503;\n  }\n"

Вниз-вверх для переконфигурирования.

Перенос данных

https://docs.gitlab.com/administration/backup_restore/restore_gitlab/

Если что, обратно docker compose down и раскладываем файлы:

  • /srv/gitlab/configgitlab-secrets.json
  • /srv/gitlab/data/backups – наш бэкап, например 1751304392_2025_06_30_18.1.1_gitlab_backup.tar
  • /srv/gitlab/data/gitlab-rails/shared/encrypted_settings – мой зашифрованный файл доступов к SMTP smtp.yaml.enc
  • /srv/gitlab/data/redisdump.rdb

Запускаем GitLab в фоне:

docker compose up -d

Не совсем понятно, сколько ждать, видимо пока не появятся HTTP-сервисы в Traefik и/или когда начнет отвечать веб. Отключаем сервисы:

docker exec gitlab gitlab-ctl stop puma
docker exec gitlab gitlab-ctl stop sidekiq

Предлагается на всякий случай проверить состояние:

docker exec gitlab gitlab-ctl status

Восстанавливаем данные («хвост» имени файла бэкапа не указывается):

docker exec -t gitlab gitlab-backup restore BACKUP=1751304392_2025_06_30_18.1.1

После вроде как успешного восстановления я сделал стоп-старт через compose.

Если все в порядке, можно будет зайти под root в админ-панель GitLab и включить обратно все cron-задания. В очередной раз docker compose down, убираем nginx['custom_gitlab_server_config'] и наконец запускаем все обратно. Уфф. tired

Для очистки совести можно выполнить пару проверок:

docker exec -t gitlab gitlab-rake gitlab:check SANITIZE=true
docker exec -t gitlab gitlab-rake gitlab:doctor:secrets

По идее обе должны пройти успешно.

Поскольку Nginx по сути тоже работает обратным прокси для вышеупомянутой «рабочей лошадки», то целых два прокси (+Traefik), а на самом деле три, поскольку лошадь – тоже прокси, по-моему перебор. Но увы – если отключить Nginx и заставить коня слушать TCP, с которым будет работать Traefik, то перестает работать вебсокет. Теоретически работать можно, но неудобно – например раздел активности в проблемах (которые issue) «на лету» не обновляется, так что выкинуть мой «любимый» Nginx к сожалению не получилось.

Финальная конфигурация

Для удобства я решил подытожить, что же все-таки у меня вышло.

.env:

GITLAB_HOME=/srv/gitlab

compose.yaml:

services:
  gitlab:
    image: gitlab/gitlab-ce:latest
    container_name: gitlab
    restart: always
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        # Add any other gitlab.rb configuration here, each on its own line
        external_url 'https://git.example.com'
        letsencrypt['enable'] = false
        nginx['listen_port'] = 80
        nginx['listen_https'] = false
        gitlab_rails['gitlab_shell_ssh_port'] = 2424
    ports:
      - '2424:22'
    expose:
      - '8090'
    labels:
      - traefik.enable=true
      - traefik.http.routers.gitlab.rule=Host(`git.example.com`)
      - traefik.http.routers.gitlab.service=gitlab
      - traefik.http.routers.gitlab.middlewares=compress
      - traefik.http.services.gitlab.loadbalancer.server.port=80
      - traefik.http.routers.pages.rule=Host(`pages.example.com`)
      - traefik.http.routers.pages.service=pages
      - traefik.http.routers.pages.middlewares=compress
      - traefik.http.services.pages.loadbalancer.server.port=8090
    volumes:
      - '$GITLAB_HOME/config:/etc/gitlab'
      - '$GITLAB_HOME/logs:/var/log/gitlab'
      - '$GITLAB_HOME/data:/var/opt/gitlab'
    shm_size: '256m'

networks:
  default:
    name: traefik
    external: true

gitlab.rb (без учета комментариев):

egrep -v "^$|^[[:space:]]*[#;]" /srv/gitlab/config/gitlab.rb
gitlab_rails['smtp_enable'] = true
gitlab_rails['smtp_address'] = "smtp.mail.ru"
gitlab_rails['smtp_port'] = 465
gitlab_rails['smtp_authentication'] = "login"
gitlab_rails['smtp_tls'] = true
gitlab_rails['smtp_enable_starttls_auto'] = false
gitlab_rails['smtp_openssl_verify_mode'] = 'peer'
gitlab_rails['gitlab_email_from'] = 'noreply@example.com'
gitlab_rails['gitlab_email_reply_to'] = 'noreply@example.com'
gitlab_rails['omniauth_providers'] = [
  {
    name: "bitbucket",
    app_id: "xxxxxxxxxxxxxxxxxx",
    app_secret: "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy",
    url: "https://bitbucket.org/"
  }
]
gitlab_rails['backup_keep_time'] = 604800
registry['enable'] = false
puma['worker_processes'] = 0
sidekiq['concurrency'] = 8
nginx['gzip_enabled'] = false
nginx['real_ip_trusted_addresses'] = ['172.16.0.0/12']
pages_external_url 'https://pages.example.com'
gitlab_pages['enable'] = true
gitlab_pages['listen_proxy'] = "0.0.0.0:8090"
gitlab_pages['namespace_in_path'] = true
pages_nginx['enable'] = false
prometheus_monitoring['enable'] = false

Напомню, что доступы к SMTP у меня хранятся в типа секретном файле /srv/gitlab/data/gitlab-rails/shared/encrypted_settings/smtp.yaml.enc.

Копия документации

https://docs.gitlab.com/administration/docs_self_host/

Здесь все просто – очередной /srv/gldocs/compose.yaml с учетом Traefik (хотя в принципе можно было бы просто объявить вторым сервисом GitLab):

services:
  gitlab_docs:
    image: registry.gitlab.com/gitlab-org/technical-writing/docs-gitlab-com/archives:18.1
    restart: unless-stopped
    labels:
      - traefik.enable=true
      - traefik.http.routers.gldocs.rule=Host(`gldocs.dmkos.ru`)
      - traefik.http.routers.gldocs.service=gldocs
      - traefik.http.routers.gldocs.middlewares=compress
      - traefik.http.services.gldocs.loadbalancer.server.port=4000

networks:
  default:
    name: traefik
    external: true

Я не переопределяю ссылку на документацию в самом GitLab (админ-панель, Settings – Preferences, Help page, Documentation pages URL). В свое время меня выбесили предупреждения о куках, а еще и редирект на текущую версию не работал. Впрочем все это видимо починили – сейчас работает, как положено.

GitLab Runner

По отработанной схеме создадим директории:

mkdir -p /srv/runner/config
cd /srv/runner

И compose.yaml по аналогии с предлагаемой в документации docker run:

services:
  gitlab-runner:
    image: gitlab/gitlab-runner:latest
    container_name: gitlab-runner
    restart: always
    volumes:
      - ./config:/etc/gitlab-runner
      - /var/run/docker.sock:/var/run/docker.sock:ro

Как обычно, пробрасываем в контейнер директорию конфигурации, а еще в данном случае необходим сокет Докера (я все-таки решил указать режим только для чтения). Подключать сервис к сети Трафика не нужно, он не должен быть доступен извне. Тут бы, конечно, тоже применить прокси для сокета, но у меня ничего не получилось wall – Докер вроде бы что-то делает, но runner потом не видит запущенные контейнеры или вообще отваливается с ошибкой 143 при подготовке окружения (хотя казалось бы, при чем тут профиль). А переключать бегуна в пользовательский режим тоже особого смысла нет, поскольку опять-таки нужен доступ к «розетке», а это в свою очередь означает довольно обширные привилегии.

Восстанавливаю .runner_system_id и config.toml в директорию config. В этих ваших интернетах предлагается в настройках самого бегуна в очередной раз пробросить несчастный сокет:

volumes = ["/cache", "/var/run/docker.sock:/var/run/docker.sock"]

Но по-моему необходимости в этом нет. По крайней мере в случае статических Pages все работает со стандартной настройкой volumes = ["/cache"], так что у себя ничего менять не стал.

Как обычно, docker compose up -d, и через некоторое время сможем проверить:

docker exec -t gitlab-runner gitlab-runner list
docker exec -t gitlab-runner gitlab-runner verify

К сожалению программа по умолчанию видимо не задана, поэтому приходится дублировать имя контейнера (первый gitlab-runner) и имя команды (второй gitlab-runner). compress.compress, да.

Но самое главное – перепроверить ранее настроенный CI/CD, в моем случае – публикацию Pages. Для этого делаем какой-нибудь коммит и наблюдаем Build – Jobs репозитория. Как ни странно, все получилось.

Резервное копирование

В принципе схема резервирования не особо отличается от случая с установкой GitLab непосредственно на хост. Необходимо вручную сохранить многочисленные compose.yaml (+ .env) и конфиги:

  • /srv/gitlab/config
  • /srv/gitlab/data/gitlab-rails/shared/encrypted_settings/smtp.yaml.enc (напомню, что я зачем-то шифрую доступы к SMTP-серверу)
  • /srv/runner/config
  • /srv/traefik/config (возможно кроме acme.json)

А с учетом Докера cron для архивирования GitLab как такового будет выглядеть так:

0 2 * * * /usr/bin/docker exec -t gitlab gitlab-backup create CRON=1

Путь к архивам теперь /srv/gitlab/data/backups.

rclone из apt почему-то не поддерживает облачный диск mega.nz, поэтому скачиваем и устанавливаем актуальную версию с сайта, например:

wget https://downloads.rclone.org/v1.70.2/rclone-v1.70.2-linux-amd64.deb
dpkg -i rclone-v1.70.2-linux-amd64.deb

Далее, как обычно, с помощью rclone config настраиваю доступ, для определенности под именем meganz, и добавляю примерно такое задание:

0 4 * * * /usr/bin/rclone sync /srv/gitlab/data/backups meganz:/gitlab/backups

Обновление

Первоначально я указал конкретную версию GitLab, чтобы корректно восстановить данные со старого сервера. Останавливаем приложение и меняем метку образа на latest:

    image: gitlab/gitlab-ce:latest

Запускаем обратно.

Непосредственно для обновления в документации предлагаются такие команды:

cd /srv/gitlab
docker compose pull
docker compose up -d

Разумеется, в идеале предварительно надо бы создать архив.

Аналогичным образом обновляется и бегун. С копией документации чуть сложнее – там нет такой метки. Посему: «вниз», меняем тег версии, «вверх». С точки зрения обновлений, конечно, менеджер пакетов проще – тот же dnf update обновляет все сам. Теперь же придется самому каким-то образом отслеживать появление новых версий, а потом еще и удалять ненужные образы.

Заключение

Вот так я перенес GitLab с Oracle Linux 8 в Докер. Благодаря Traefik решилась проблема с сертификатами, а еще теперь можно смело размещать на сервере что-то помимо тануки. Хотя в общем случае это наверное неправильно.

Все еще остается открытым вопрос с реестром контейнеров. Как только, что называется, так сразу. write


Категория: Решение проблем | Опубликовано 03.08.2025 | Редакция от 14.08.2025

Похожие материалы

Переезд реестра контейнеров GitLab в S3

Ранее я писал о MinIO в качестве хранилища реестра контейнеров, но в случае VDS/VPS такой вариант экономически не выгоден: чуть ли не на порядок дешевле воспользоваться услугой аренды S3 у какого-нибудь облачного провайдера. Что я и решил сделать, ведь место на сервере стало очень быстро заканчиваться. Заодно мигрируем прокси зависимостей, LFS и всякое такое.


Комментарии, обсуждение