Instalar o MySQL leva um comando. A maior parte do trabalho vem depois: um usuário para o app, um jeito de chegar ao banco a partir do seu computador sem colocar a porta 3306 na internet, ajustes de memória para um servidor pequeno e backups que rodam sem você. Este guia cobre os quatro no Ubuntu 24.04. Cada passo foi executado na nossa imagem do Ubuntu 24.04, e os números abaixo vêm desse servidor.
Rode os comandos com um usuário que tenha sudo. O guia de configuração do Ubuntu cria um.
1. Instale
sudo apt update
sudo apt install -y mysql-server
mysql --version
A instalação levou 51 segundos no nosso servidor e trouxe o MySQL 8.0.46. Ele já inicia, volta a iniciar a cada boot e escuta só no próprio servidor: 127.0.0.1:3306 para conexões normais e 127.0.0.1:33060 para o protocolo X, mais novo. Por enquanto nada fica acessível de fora.
2. Crie um banco e um usuário
A conta root do MySQL usa autenticação por socket no Ubuntu, então sudo mysql entra sem senha. Crie um banco e um usuário para o seu app:
sudo mysql
CREATE DATABASE app CHARACTER SET utf8mb4;
CREATE USER 'app'@'localhost' IDENTIFIED BY 'a-long-random-password';
GRANT ALL PRIVILEGES ON app.* TO 'app'@'localhost';
EXIT;
Teste do jeito que o app vai conectar:
mysql -u app -p app -e "SELECT CURRENT_USER();"
Deve aparecer app@localhost. Uma senha errada recebe ERROR 1045 (28000): Access denied for user 'app'@'localhost'. O usuário pode fazer tudo dentro de app e nada fora dele.
3. Conecte do seu computador: túnel SSH
Para usar um cliente de desktop como DBeaver, MySQL Workbench ou a linha de comando mysql na sua máquina, não abra porta nenhuma. Redirecione uma pelo SSH:
ssh -L 3307:127.0.0.1:3306 deploy@YOUR.SERVER.IP
Num plano NAT, acrescente -p e a porta SSH pessoal do painel. Enquanto essa janela estiver aberta, aponte o cliente para o host 127.0.0.1, porta 3307, usuário app. Usamos 3307 do lado local para não bater com um MySQL que você talvez rode no seu computador. No nosso teste o servidor respondeu pelo túnel na hora, enquanto uma conexão direta ao endereço dele na 3306 foi recusada.
4. Acesso direto para outro servidor
Às vezes o app roda num segundo servidor e precisa conectar direto. Para isso o banco precisa de um plano com IPv4 próprio, porque um plano NAT só redireciona a sua porta SSH. São três mudanças, todas limitadas ao endereço do servidor do app (aqui 203.0.113.20):
sudo sed -i 's/^bind-address.*/bind-address = 0.0.0.0/' /etc/mysql/mysql.conf.d/mysqld.cnf
sudo systemctl restart mysql
sudo ufw allow from 203.0.113.20 to any port 3306 proto tcp
CREATE USER 'app'@'203.0.113.20' IDENTIFIED BY 'a-long-random-password';
GRANT ALL PRIVILEGES ON app.* TO 'app'@'203.0.113.20';
Se o ufw ainda não estiver ativo, libere o SSH primeiro (sudo ufw allow OpenSSH) e depois rode sudo ufw enable. O guia do ufw tem os detalhes.
Conferimos as duas travas separadamente. Com a regra do firewall, mas sem um usuário do MySQL correspondente, o servidor recusou com error 1130: Host '…' is not allowed to connect to this MySQL server. Com o usuário criado, a conexão passou. Sem a regra do firewall, a conexão simplesmente estourou o tempo limite. Qualquer outro endereço na internet recebe esse mesmo tempo esgotado.
5. Use menos RAM
Em repouso, sem dados próprios, o MySQL 8.0 usava 383 MB no nosso servidor. A maior parte é o Performance Schema, um recurso de diagnóstico que reserva memória antecipadamente. Num VPS pequeno, desligue-o e defina você mesmo o tamanho do cache principal:
sudo tee /etc/mysql/mysql.conf.d/zz-small-vps.cnf >/dev/null <<'EOF'
[mysqld]
performance_schema = OFF
innodb_buffer_pool_size = 256M
max_connections = 50
EOF
sudo systemctl restart mysql
ps -o rss= -C mysqld
Depois de reiniciar, ele usava 165 MB. O buffer pool guarda em cache seus dados e índices, e 256 MB servem para um servidor de 2 GB que também roda o app. Aumente se o banco for a principal coisa do servidor e já tiver passado do tamanho do cache. max_connections = 50 é de sobra para um app com pool de conexões. Você perde as estatísticas do performance_schema. Ligue de novo se um dia precisar investigar consultas lentas a fundo.
6. Backups noturnos
O mysqldump --single-transaction tira uma cópia consistente das tabelas InnoDB sem travar o app. Coloque-o num script pequeno:
sudo tee /usr/local/bin/mysql-backup >/dev/null <<'EOF'
#!/bin/bash
set -euo pipefail
umask 077
mkdir -p /var/backups/mysql
mysqldump --single-transaction --routines --triggers app | gzip > /var/backups/mysql/app-$(date +%F).sql.gz
find /var/backups/mysql -name 'app-*.sql.gz' -mtime +14 -delete
EOF
sudo chmod 755 /usr/local/bin/mysql-backup
echo '0 3 * * * root /usr/local/bin/mysql-backup' | sudo tee /etc/cron.d/mysql-backup
sudo /usr/local/bin/mysql-backup && sudo ls -l /var/backups/mysql
Cada linha tem um motivo:
umask 077deixa o dump legível só para o root (-rw-------), já que ele guarda todos os seus dados.pipefailfaz o script inteiro falhar se omysqldumpfalhar. Sem isso, ogzipgravaria um arquivo minúsculo e vazio e diria que deu tudo certo. Quebramos o comando de propósito e o script terminou com código 2, como deve.find … -mtime +14 -deletemantém duas semanas de cópias.- O script roda como root, então o
mysqldumpentra pelo socket e nenhuma senha fica num arquivo.
Backup que você nunca restaurou ainda não conta. Restaure num banco temporário e olhe os dados:
sudo mysql -e "CREATE DATABASE app_restore"
gunzip -c /var/backups/mysql/app-2026-10-25.sql.gz | sudo mysql app_restore
sudo mysql -e "SHOW TABLES FROM app_restore"
sudo mysql -e "DROP DATABASE app_restore"
No nosso teste, a tabela restaurada tinha as mesmas linhas da original. Cópias no mesmo disco não protegem se você perder o servidor, então leve-as de vez em quando para outra máquina com scp ou restic. O Managed Backups soma pontos de restauração diários do servidor inteiro.
Qual plano
O Micro (2 vCPU, 2 GB, 25 GB, $5/mês) aguenta o MySQL ajustado como acima mais um app web. Se o banco em si passar de alguns gigabytes, o Small, com 4 GB e 35 GB por $8/mês, deixa você dar 1 GB ou mais ao buffer pool. Os dois são planos NAT, e o túnel SSH do passo 3 funciona neles. O passo 4 exige IPv4 próprio: o Micro-IP custa $10/mês.
Prefere PostgreSQL? O guia do PostgreSQL segue o mesmo roteiro. Todos os planos estão em VPS, e o pagamento é em USDT, USDC ou ETH nas redes Base, Ethereum ou Polygon.
Comentários
Nenhum comentário ainda. Seja o primeiro.