PostgreSQL и MySQL на VPS: от установки до бэкапа
Рано или поздно приложению нужно хранить данные надёжнее, чем в текстовом файле рядом с кодом. Разберёмся, как поставить СУБД на сервер и не открыть её всему интернету.
Что выбрать: PostgreSQL или MySQL
Для подавляющего большинства проектов разница не принципиальна — обе базы бесплатны, стабильны и умеют всё нужное для типового веб-приложения. Выбор чаще определяет не техника, а окружение.
PostgreSQL обычно берут, если приложение написано на Python/Django, Ruby, Go, или если заранее известно, что данные будут сложными — с JSON-полями, геоданными, строгими типами и сложными выборками. У Postgres более строгая и предсказуемая обработка типов данных.
MySQL (и его форк MariaDB) исторически стандарт для PHP-стека — WordPress, большинство CMS и многие готовые PHP-приложения ожидают именно его. Если разворачиваете готовый продукт, документация проекта обычно сама скажет, что нужно.
Если выбора нет и решаете сами — берите PostgreSQL, он не проигрывает MySQL ни в чём и растёт вместе с проектом. Дальше в статье покажем установку обеих, чтобы гайд подходил под любой случай.
Установка PostgreSQL
Ставим из стандартного репозитория Ubuntu — для большинства задач его версии достаточно, отдельный репозиторий PostgreSQL нужен только если требуется самая свежая мажорная версия.
sudo apt install -y postgresql postgresql-contrib
Пакет postgresql-contrib добавляет полезные расширения (например, генерацию UUID), их лучше поставить сразу. После установки служба уже запущена и добавлена в автозагрузку, но стоит проверить:
sudo systemctl status postgresql
В выводе должно быть active (running). Если что-то не так — здесь же в логе будет причина, обычно связанная с портом или правами на каталог данных.
Создаём базу и пользователя в PostgreSQL
При установке создаётся системный пользователь postgres — это администратор СУБД, привязанный к одноимённому системному аккаунту Linux. Зайти под ним можно только локально, с сервера, без пароля — это называется peer-аутентификацией: PostgreSQL сверяет имя текущего Linux-пользователя с именем роли в базе.
Внутри открывается консоль psql. Создадим роль (пользователя) с паролем и отдельную базу данных под приложение:
CREATE DATABASE appdb OWNER appuser;
GRANT ALL PRIVILEGES ON DATABASE appdb TO appuser;
\q
Роль с паролем — это уже md5/scram-sha-256 аутентификация: при подключении по паролю PostgreSQL сверяет не системного пользователя, а хеш пароля, хранящийся в базе. Именно так будет заходить приложение — не через peer, а по логину и паролю, как обычно и ожидают драйверы баз данных.
Проверить подключение от имени нового пользователя:
Флаг -h 127.0.0.1 здесь важен: он заставляет клиента подключаться по TCP с паролем, а не пытаться через peer-механизм локального сокета.
Установка MySQL
Если приложению нужен именно MySQL (или его совместимый форк MariaDB), ставим так:
sudo apt install -y mysql-server
sudo systemctl enable --now mysql
Сразу после установки стоит пройти базовую настройку безопасности — она задаст пароль root, уберёт тестовую базу и анонимных пользователей:
Скрипт задаст несколько вопросов — на всё, что касается удаления тестовых данных и анонимного доступа, отвечайте Y. Дальше создаём базу и пользователя для приложения:
CREATE DATABASE appdb CHARACTER SET utf8mb4;
CREATE USER 'appuser'@'localhost' IDENTIFIED BY 'сложный_пароль';
GRANT ALL PRIVILEGES ON appdb.* TO 'appuser'@'localhost';
FLUSH PRIVILEGES;
EXIT;
Обратите внимание на 'appuser'@'localhost' — в MySQL пользователь привязан не только к имени, но и к хосту, с которого он подключается. Это отдельный механизм ограничения доступа, независимый от файрвола, и его стоит держать в уме, если позже понадобится доступ извне.
Доступ извне и firewall
По умолчанию обе СУБД слушают только локальный интерфейс: PostgreSQL — параметр listen_addresses = 'localhost' в /etc/postgresql/*/main/postgresql.conf, MySQL — bind-address = 127.0.0.1 в /etc/mysql/mysql.conf.d/mysqld.cnf. Это осознанное решение разработчиков дистрибутива, а не недосмотр — базу данных не открывают в интернет.
ufw allow 5432 без ограничения по IP. База данных, доступная всему миру по паролю, — вопрос времени до подбора или утечки, даже при сложном пароле: она станет постоянной целью автоматического сканирования.Если приложение и база физически на одном сервере — ничего менять не нужно, обмен идёт через localhost, и порт наружу вообще не нужен. Если же приложение работает на другом сервере, есть два нормальных варианта:
1. Разрешить доступ только с конкретного IP второго сервера через ufw:
2. Более надёжный вариант — не открывать порт наружу вообще, а связать серверы приватной сетью через VPN, и подключаться к базе по внутреннему адресу. Как поднять такую сеть, разобрано в отдельном гайде: настройка VPN на WireGuard.
Подключение из приложения
Большинство фреймворков и библиотек принимают адрес базы одной строкой — DATABASE_URL. Для PostgreSQL она выглядит так:
Для MySQL — аналогично, меняется только схема и порт:
Пароль в такой строке — обычный секрет, и хранить его нужно так же, как любой другой: в файле .env, который не попадает в репозиторий, а не прямо в коде приложения. Если приложение уже запущено в Docker — подробнее о том, зачем нужен .env и как его подключают к контейнеру, есть в гайде про Docker.
Резервное копирование
База без бэкапа — вопрос времени до потери данных: ошибка в миграции, неудачное обновление или банальный человеческий фактор. Сделать разовый дамп несложно.
Для PostgreSQL:
Для MySQL:
Обе команды создают текстовый файл со структурой и данными — восстановить базу из него можно на любом сервере с той же СУБД. Ручной дамп подходит для разовой подстраховки перед рискованной операцией, но для регулярного расписания и автоматической ротации старых копий использовать cron напрямую неудобно и легко забыть — подробная настройка автоматических бэкапов разобрана в отдельном гайде: резервное копирование по расписанию.
Типовые ошибки
FATAL: password authentication failed for user— опечатка в пароле, либо вpg_hba.confдля этого пользователя настроенpeer, а неmd5/scram-sha-256.Access denied for user 'appuser'@'localhost'— пользователь MySQL создан для другого хоста (например,'appuser'@'%'вместо'localhost'), либо не выполненFLUSH PRIVILEGES.- Правки в
postgresql.confилиmysqld.cnfне применились — после изменения конфигурационного файла нуженsudo systemctl restart postgresql(илиmysql), простого сохранения файла недостаточно. could not bind IPv4 address/ порт уже занят — на сервере уже запущена другая СУБД или второй экземпляр той же самой, проверяется командойsudo ss -tlnp | grep -E '5432|3306'.- Приложение подключается по
localhost, а не по127.0.0.1, и падает с ошибкой сокета — некоторые драйверы трактуют эти адреса по-разному; если что-то не работает, стоит явно попробовать оба варианта.
Что дальше
База готова принимать данные — самое время дать ей применение: развернём файловое хранилище Nextcloud, которое как раз использует такую базу данных →