March 12, 2025

Beget VPS: Ubuntu 24.04 + Rails 8

Статья представляет собой пошаговое руководство к развёртыванию Rails платформы на предоставленном хостинге Beget на операционной системе Ubuntu. Использованы актуальные компоненты по состоянию на март 2025 года.

Параметры арендуемого виртуального сервера:

  • 1 ядро процессора
  • 2 Гб оперативной памяти. На 1 Гб нормально работал Ubuntu 20.04 + Rails 7, в новой связке, из-за нехватки памяти Ubuntu 24.04 стал убивать процесс make при установке Ruby, пришлось добавить. Считайте 2 Гб памяти минимальным требованием
  • 10 Гб NVMe
  • 1 внешний IP адрес, 250 Мб/сек. канал

Содержание:

  • Ubuntu 24.04

- Настройка SSH

- Установка пароля и смена имени сервера

- Добавление пользователя

- Дистанционное обслуживание БД

- Резервное копирование БД

- Настройка HTTPS

- Конфигурация Nginx

- Генерация приложения Rails

- Настройка Puma

Ubuntu 24.04

Параметры установки Ubuntu 24.04 контролируются провайдером, пользователь получит готовую систему с доступом по SSH. Стоит оговорить некоторые моменты.

Настройка SSH

[Наверх]

Сразу надо настроить SSH-аутентификацию по сертификату вместо пароля, так будет безопаснее и проще подключаться. Клиент, с которого будем подключаться к нашему серверу также на Ubuntu 24.04. Сделаем на клиенте необходимые ключи шифрования:

# Проверяем наличие папки .ssh, если её нет, то создаём
$ mkdir ~/.ssh

# Генерим пару: закрытый и публичный ключи
$ ssh-keygen -q -t ed25519 -N '' -f $HOME/.ssh/remote_access

# Проверяем что в папке .ssh появились 2 файла:
# "remote_access"
# "remote_access.pub"
$ ls ~/.ssh

# Выводим содержимое публичного ключа на консоль для копирования
$ cat .ssh/remote_access.pub

На страничке установки/переустановки операционной системы у Beget есть кнопка "Добавить новый SSH-ключ". Нажимаем её и вставляем содержимое публичного ключа из консоли. Далее у нас появляется блок dev@rails, активируем его как способ аутентификации. При развёртывании Ubuntu хостинг пропишет наш ключик в файле /root/.ssh/authorized_keys.

Ключ сделан на машине с именем rails под польователем dev, поэтому такая запись. Имя dev не самое удачное, в корне Ubuntu существует одноимённая папка.

После установки Ubuntu хостинг также пришлёт на почту пароль root. Если мы будем пользоваться консолью Beget или Putty то он понадобится. А так, со своей машины Ubuntu соединяемся с хостом через SSH командой:

$ ssh -i ~/.ssh/remote_access root@<IP_VPS>

Параметр -i задаёт явное имя закрытого ключа, без него ssh будет искать файл "id_rsa".

Сконфигурировал SSH следующим образом:

$ nano /etc/ssh/sshd_config

# /etc/ssh/sshd_config
PasswordAuthentication no
PermitRootLogin prohibit-password
PubkeyAuthentication yes
AuthorizedKeysFile	%h/.ssh/authorized_keys

$ systemctl restart ssh

Значение prohibit-password позволяет подключаться под root, но только по ключу.

Установка пароля и смена имени сервера

[Наверх]

# Смена пароля root (желательно)
$ passwd
# Смена имени сервера
$ hostnamectl set-hostname <MY_NAME_HOST>
$ nano /etc/hosts
# Добавить строчку
127.0.1.1       <MY_NAME_HOST>   <MY_NAME_HOST>

Добавление пользователя

[Наверх]

Эксплуатация root в постоянном режиме не приветствуется, так что создадим отдельного пользователя, под которым будет работать наш сервер. Назовём его picker:

# Добавление нового пользователя
$ adduser picker

# Добавление в группу привилегированных пользователей
$ adduser picker sudo

# В домашней директории пользователя создаём папку для хранения ключей
$ mkdir -p /home/picker/.ssh
$ chown picker:picker /home/picker/.ssh

# Копируем у root файлик authorized_keys, чтобы соединяться по SSH
$ cp -i /root/.ssh/authorized_keys /home/picker/.ssh/authorized_keys

# Завершаем сессию root и входим под пользователем picker
$ exit
$ ssh -i ~/.ssh/remote_access picker@<IP_VPS>

Все последующие действия выполняем под пользователем picker, учётной записью root больше не пользуемся.

UFW

[Наверх]

Поработаем ещё на защитой сервера и задействуем простой межсетевой экран UFW. По умолчанию он неактивен, а если мы его включим, то стандартные настройки запретят все входящие соединения и разрешат все исходящие. Поэтому перед включением добавим несколько протоколов: SSH или 22 порт (обязательно, мы через него подключаемся к машине), HTTP или 80 порт, через который работает web-приложение, HTTPS или 443 порт, защищённый протокол web-приложения. Далее все команды с комментариями:

# Проверяем, установлен ли вообще UFW
$ ufw --version
ufw 0.36.2
Copyright 2008-2023 Canonical Ltd.

# В каком состоянии находится UFW
$ sudo ufw status
Status: inactive

# Добавляем нужные нам правила
$ sudo ufw allow ssh
$ sudo ufw allow http
$ sudo ufw allow https

# Включаем UFW (после перезагрузки также будет работать)
$ sudo ufw enable

# В каком состоянии находится UFW + действующие правила
$ sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
New profiles: skip

To                         Action      From
--                         ------      ----
22/tcp                     ALLOW IN    Anywhere
80/tcp                     ALLOW IN    Anywhere
443/tcp                    ALLOW IN    Anywhere
22/tcp (v6)                ALLOW IN    Anywhere (v6)
80/tcp (v6)                ALLOW IN    Anywhere (v6)
443/tcp (v6)               ALLOW IN    Anywhere (v6)

Что ещё может понадобиться:

Удаление правила - sudo ufw delete allow http

Отключение межсетевого экрана - sudo ufw disable

PostgreSQL

[Наверх]

После того как Beget развернул нам операционку мы ещё не разу её не обновляли, поэтому после sudo apt update Ubuntu скорее всего скажет, что доступно много пакетов обновления, возможно даже связанных с безопасностью. Поставим их все и для верности выполним перезагрузку:

$ sudo apt update
$ sudo apt upgrade
$ sudo reboot

В качестве базы данных будем использовать PostgreSQL. Ставим последнюю доступную версию и пакет разработчика следующей командой:

$ sudo apt install postgresql libpq-dev

Убеждаемся, что служба postgresql запущена.

$ sudo systemctl status postgresql

Выполним ещё некоторые предварительные настройки базы данных. Входим в сессию служебного пользователя postgres:

$ sudo su - postgres

Входим в командную строку PostgreSQL:

$ psql

Устанавливаем пароль служебного пользователя postgres (можно писать USER вместо ROLE, но USER считается устаревшей конструкцией):

postgres=# ALTER ROLE postgres WITH PASSWORD '<пароль>';

Добавим ещё одного пользователя СУБД - trader, под которым будет работать приложение, и укажем его пароль:

postgres=# CREATE ROLE trader WITH LOGIN PASSWORD '<пароль>';

Разрешим пользователю создавать базы:

postgres=# ALTER ROLE trader WITH CREATEDB;

Выходим из командной строки PostgreSQL:

postgres=# exit

Выходим из сессии служебного пользователя postgres.

$ exit

Дистанционное обслуживание БД

[Наверх]

Если нужно удалённое подключение к базе данных, то следует добавить правило межсетевого экрана, для той машины с которой будет выполняться подключение (вместо 192.168.123.123 нужно подставить свой IP адрес):

$ sudo ufw allow proto tcp from 192.168.123.123 to any port 5432

Или создать более простое правило, разрешающее подключение с любых адресов:

$ sudo ufw allow 5432/tcp

Далее требуется изменить основной файл конфигурации:

# Определяем путь к файлу конфигурации
$ sudo su - postgres -c "psql -c 'SHOW config_file;'"
               config_file               
---------------------------------------
 /etc/postgresql/16/main/postgresql.conf
(1 row)

$ sudo nano /etc/postgresql/16/main/postgresql.conf

# /etc/postgresql/16/main/postgresql.conf
listen_addresses = '*'

Чтобы изменения вступили в силу, перезапускаем службу postgresql:

$ sudo systemctl restart postgresql

Проверим что настройка применилась:

$ ss -tunlp | grep 5432
tcp   LISTEN 0      200          0.0.0.0:5432      0.0.0.0:*
tcp   LISTEN 0      200             [::]:5432         [::]:* 

Далее требуется изменить файл конфигурации pg_hba.conf.

# Определяем путь к файлу hba_file
$ sudo su - postgres -c "psql -c 'SHOW hba_file;'"
              hba_file               
-------------------------------------
 /etc/postgresql/16/main/pg_hba.conf
(1 row)

$ sudo nano /etc/postgresql/16/main/pg_hba.conf
# /etc/postgresql/16/main/pg_hba.conf добавить строку:

# IPv4 local connections:
host    all    all    192.168.123.123/32    scram-sha-256

Если хотим разрешить подключения с любых адресов, то вместо 192.168.123.123/32 надо написать 0.0.0.0/0. Далее ещё раз перезапустим службу postgresql:

$ sudo systemctl restart postgresql

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

[Наверх]

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

Для приложения предполагается:

  • пользователь от имени которого работает сервер - picker;
  • пользователь от имени которого работаем с PostgreSQL - trader;
  • база данных приложения - market;

Для целей резервного копирования добавим пару папок backup и scripts в домашнюю директорию:

$ mkdir backup
$ mkdir scripts

Если сходу попробовать сделать копию БД командой pg_dump, то будет примерно такая ошибка:

pg_dump: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: FATAL: Peer authentication failed for user "trader"

Ошибка происходит из-за попытки системы выполнить команду в контексте текущего пользователя - picker, даже несмотря на явное указание пользователя в команде. А для корректного сопоставления имён необходимо добавить следующие строчки в пару файлов:

$ sudo nano /etc/postgresql/16/main/pg_ident.conf

# MAPNAME       SYSTEM-USERNAME         PG-USERNAME
picker          picker                  trader

Вместо picker напишите своё имя, которое вернёт команда whoami.

$ sudo nano /etc/postgresql/16/main/pg_hba.conf

# TYPE     DATABASE   USER       ADDRESS       METHOD
local      all        postgres                 peer
local      all        trader                   peer map=picker

Перезагружаем службу:

$ sudo systemctl restart postgresql

После этого команда pg_dump отрабатывает корректно:

$ pg_dump -U trader market | gzip > backup/pgsql_$(date "+%Y-%m-%d").sql.gz

Для автоматизации резервного копирования написал скрипт, со следующим содержимым:

$ nano scripts/backup.sh

#!/bin/sh
PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin

PGPASSWORD=<your password>
export PGPASSWORD
pathB=backup
dbUser=trader
database=market

find $pathB -type f -ctime +10 -delete
pg_dump -c -U $dbUser $database | gzip > $pathB/pgsql_$(date "+%Y-%m-%d_%H-%M").sql.gz

unset PGPASSWORD

Первая команда в скрипте удалит из папки backup файлы старше 10 дней, вторая сделает сжатую резервную копию.

Скрипт делаем исполняемым и пробуем запускать, проверяя на отсутствие ошибок и появление сжатых дампов баз данных в папке backup:

$ chmod +x scripts/backup.sh
$ ./scripts/backup.sh

Настроим запуск скрипта каждые 4 часа:

$ crontab -e
0 */4 * * * /home/picker/scripts/backup.sh # pg_dump "market"

Полноценное резервное копирование лучше делать на другой носитель, здесь дампы больше нужны для целей разработки, чтобы оперативно получить БД из производственной среды.

В среде разработки конфиги никакие не правил, так что восстановление распакованной базы данных выполняется такой командой:

$ sudo -u postgres psql -d market < pgsql_2025-06-16_12-50.sql

Mise & Ruby

[Наверх]

https://gorails.com/setup/ubuntu/24.04

Теперь собственно к средствам программирования. Информация преимущественно взята с хорошей странички gorails, там можно найти инструкции для установки Rails под разные операционные системы. Устанавливаем необходимые пакеты для компиляции Ruby:

$ sudo apt-get update
$ sudo apt install build-essential rustc libssl-dev libyaml-dev zlib1g-dev libgmp-dev

Раньше различными версиями Ruby управляли с помощью менеджера пакетов RVM (Ruby Version Manager). Более современное решение - Mise (наследие Rbenv), который управляет не только пакетами Ruby, но и например Golang, Node.js, Yarn, Rust и многими другими.

Следующими командами мы установим Mise:

$ curl https://mise.run | sh
$ echo 'eval "$(~/.local/bin/mise activate)"' >> ~/.bashrc
$ source ~/.bashrc

# Mise может проверить себя
$ mise doctor

Установка актуального Ruby из 3-ей ветки с помощью Mise (ставился в моём случае 15 мин, а на 1 Гб эта команда и вовсе не могла завершиться):

$ mise use --global ruby@3

Убедимся что Ruby установлен:

$ ruby --version
ruby 3.4.3 (2025-04-14 revision d0b7e5b6a0) +PRISM [x86_64-linux]

Вместе с Ruby также установится его менеджер пакетов Rubygems, обновим его версию до последней:

$ gem update --system

Для полноценной работы с Rails нам может понадобится Node.js и Yarn. Поставим их последние версии следующими командами:

$ mise use --global node
$ mise use --global yarn

Git

[Наверх]

Git в системе стоял:

$ git -v
git version 2.43.0

Увязываем локальный git с учётной записью на Github:

$ git config --global color.ui true
$ git config --global user.name "YOUR NAME"
$ git config --global user.email "YOUR@EMAIL.com"

# Генерим пару: закрытый и публичный ключи
# На этот раз для SSH соединения с сайтом github.com
$ ssh-keygen -t ed25519 -C "YOUR@EMAIL.com" -f $HOME/.ssh/github

# Выводим содержимое публичного ключа на консоль для копирования
$ cat .ssh/github.pub

Содержимое файла github.pub вставляем на своей страничке: https://github.com/settings/ssh

Успешное подключение можно проверить командой:

$ ssh -i ~/.ssh/github -T git@github.com

Rails 8.0.2

[Наверх]

Версия 8.0.2 - это то что было актуально на момент написания статьи, со временем будут появляться новые версии. Установка:

$ gem install rails -v 8.0.2

Проверяем наличие rails в системе.

$ rails -v
Rails 8.0.2

Не будем пока разворачивать тестовое приложение, как это делается во многих учебниках, а подготовим front-end.

Nginx

[Наверх]

Установка Nginx

$ sudo apt update
$ sudo apt install nginx

Добавим программу в автозагрузку, проверим присутствие в автозагрузке (должны увидеть enabled) и проверим текущий статус:

$ sudo systemctl enable nginx
$ sudo systemctl is-enabled nginx
$ sudo systemctl status nginx

Раз nginx запущен, то в браузере должны увидеть подтверждение корректной работы с любой интернет машины:

Настройка HTTPS

[Наверх]

https://certbot.eff.org/instructions?ws=nginx&os=snap

Сервер настроим на работу по протоколу HTTPS, для чего понадобится выпустить бесплатный сертификат Let's Encrypt (некоммерческий центр сертификации). Все инструкции по настройке можно взять на официальном сайте (см. выше), потребуется указать какой веб-сервер используется и какой используется менеджер пакетов. В итоге настройка получилась из таких шагов:

Установка certbot:

$ sudo apt-get install snapd
$ sudo snap install core
$ sudo snap refresh core
$ sudo snap install --classic certbot 
$ sudo ln -s /snap/bin/certbot /usr/bin/certbot

Выпуск сертификата выполнялся с помощью плагина nginx. В процессе потребуется ввести зарегистрированное доменное имя сервера:

$ sudo certbot --nginx

Сразу после этого сайт стал открываться по https:// с тем же содержимым, а с http:// срабатывает переадресация на защищённое соединение.

Сертификат выпускается на 90 дней. Задание на обновление обнаружилось по команде systemctl list-timers. Насколько оно успешно будет отрабатывать можно проверить следующей командой:

$ sudo certbot renew --dry-run

Конфигурация Nginx

[Наверх]

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

$ sudo mv /etc/nginx/sites-available/default /etc/nginx/sites-available/default.bak
$ sudo nano /etc/nginx/sites-available/default

# /etc/nginx/sites-available/default

upstream app {
  # Path to Puma TCP
  server 127.0.0.1:3000;
}

server {
    # managed by Certbot
    listen 443 ssl;
    listen [::]:443 ssl ipv6only=on;

    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
    ssl_certificate /etc/letsencrypt/live/<VPS_DOMAIN_NAME>/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/<VPS_DOMAIN_NAME>/privkey.pem;
    
    server_name <VPS_DOMAIN_NAME>;
    root home/picker/market/public;
    try_files $uri/index.html $uri @app;

    location @app {
      proxy_pass http://app;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_set_header X-Forwarded-Proto $scheme;
      proxy_set_header X-Forwarded-Ssl on;
      proxy_set_header X-Forwarded-Port $server_port;
      proxy_redirect off;
    }

    error_page 500 502 503 504 /500.html;
    client_max_body_size 4G;
    keepalive_timeout 10;
}

server {
    listen 80 ;
    listen [::]:80;

    server_name <VPS_DOMAIN_NAME>;
    return 302 https://$server_name$request_uri;
}

Все вставки <VPS_DOMAIN_NAME> надо поменять на свои данные, фрагменты Certbot-а нужно оставить, переделал только записи, добавленные для 80 порта. Далее последовательно тестируем конфигурацию Nginx на отсутствие ошибок sudo nginx -t и перезапускаем Nginx sudo systemctl restart nginx.

Основные команды службы Nginx:

  • Запуск - sudo systemctl start nginx
  • Отключение - sudo systemctl stop nginx
  • Перезапуск - sudo systemctl restart nginx
  • Перезагрузка - sudo systemctl reload nginx
  • Проверка состояния службы - sudo systemctl status nginx
  • Тестирование конфигурации - sudo nginx -t

Теперь Nginx ожидает back-end потока по адресу http://127.0.0.1:3000, пришло время создать приложение Rails.

Генерация приложения Rails

[Наверх]

Создаём новое приложение Rails:

$ rails new market -d postgresql -T --skip-solid

Настраиваем подключение к СУБД, правим файл config/database.yml:

$ cd market && nano config/database.yml

# config/database.yml:

default: &default
  host: localhost
  port: 5432
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

production:
  <<: *default
  database: market
  username: <%= Rails.application.credentials[:database][:username] %>
  password: <%= Rails.application.credentials[:database][:password] %>

Переменные database_username и database_password добавим в файл с чувствительными данными:

EDITOR=nano rails credentials:edit

# config/credentials.yml.enc
database:
  username: trader
  password: ...

Попробуем создать базу данных и убедиться, что нигде не ошиблись:

$ rake db:create
Created database 'market'

Чтобы не срабатывал Host Authorization нужно вручную указать допустимые DNS имена сервера:

$ nano config/environments/production.rb

# Add lines to: config/environments/production.rb:

Rails.application.configure do
...
  # Whitelist one hostname
  config.hosts << "<VPS_DOMAIN_NAME>"

Находясь в папке приложения запускаем сервер:

$ RAILS_ENV=production bundle exec rake assets:precompile
$ rails s -e production

После этого, зайдя на наш сайт по защищённому протоколу должны увидеть такую страничку:

Может показаться что сайт не работает. На самом деле дизайн странички указывает на то что её отдал Rails и эта страничка лежит по адресу /home/picker/market/public/404.html. Такое поведение приложение связано с тем что в приложении не определена стартовая страница, да её и нет по сути.

Можно остановить сервер Rails [Ctrl+C] и сгенерировать пустой контроллер с представлением:

$ rails g controller start index

После повторного запуска страница https://<VPS_IP>/start/index будет отдавать созданное по умолчанию представление:

Настройка Puma

[Наверх]

Наверно стоит немного объясниться на счёт Puma.

Обычно при настройке Nginx штатный веб-сервер Puma заменяется на Passenger, но в последних релизах Passenger получил приставку Enterprise и стал требовать лицензии. Не имея серьёзных амбиций по быстродействию можно продолжать использовать Puma.

https://puma.io/puma/file.systemd.html

Для среды production Puma желательно запускать в виде службы. Сделаем такую службу согласно официальной рекомендации. Для этого внутри папки /etc/systemd/system/ нужно создать новый файл puma.service, со следующим содержимым (ExecStart два варианта, оба рабочие):

$ sudo nano /etc/systemd/system/puma.service

# puma.service

[Unit]
Description=Puma HTTP Server
After=network.target

[Service]
Type=notify
WatchdogSec=10
User=picker
WorkingDirectory=/home/picker/market/
ExecStart=/home/picker/.local/share/mise/shims/puma -e production -C /home/picker/market/config/puma.rb
# ExecStart=/home/picker/.local/share/mise/shims/bundle exec puma -e production -C ./config/puma.rb config.ru -b tcp://127.0.0.1:3000
Restart=always

[Install]
WantedBy=multi-user.target

Обработаем наш файл:

# Применить изменения в файлах служб systemd
$ sudo systemctl daemon-reload

# Добавить службу в автозагрузку
$ systemctl enable puma.service

# Проверим присутствие в автозагрузке
$ sudo systemctl is-enabled puma

# Стартуем службу
$ sudo systemctl start puma

# Проверяем статус работы службы
$ sudo systemctl status puma

Если служба успешно стартовала, то наше приложение должно работать. И также должно работать после перезагрузки сервера.

Основные команды для созданной службы Puma:

  • Применить изменения в файлах служб systemd без перезагрузки всей системы: sudo systemctl daemon-reload
  • Запуск - sudo systemctl start puma
  • Отключение - sudo systemctl stop puma
  • Перезапуск - sudo systemctl restart puma
  • Проверка состояния службы - sudo systemctl status puma

Вот основное, чтобы мог бы выделить по подготовке платформы Rails на Ubuntu. Остаётся разрабатывать хорошие приложения и заливать их на подготовленный сервер.