Instalação Manual do MySQL no Debian, Arch, Fedora e openSUSE

[334] Instalação Manual do MySQL no Debian, Arch, Fedora e openSUSE

Instalação do MySQL pelo tarball genérico da Oracle em Debian, Arch, Fedora e openSUSE: as dependências que mudam entre as quatro, o layout de diretórios e a ordem correta dos chown, inicialização do data directory, my.cnf mínimo e um unit systemd com Type=notify que serve às quatro sem alteração.
DevOps

13 min de leitura

Este guia cobre a instalação do MySQL 9.7.0 puro (sem MariaDB, sem pacotes de distro) usando o tarball genérico oficial da Oracle, válido para Debian, Arch Linux, Fedora e openSUSE. O procedimento de instalação e o arquivo de serviço systemd são idênticos nas quatro distribuições — apenas as dependências variam.

Por que tarball genérico? Os pacotes de repositório das distros frequentemente instalam o MariaDB ou uma versão desatualizada do MySQL. O binário genérico da Oracle garante a versão exata que você escolheu, sem conflitos com o gerenciador de pacotes.

1. Download do Tarball Genérico

O arquivo é único para todas as distros — é um binário compilado contra glibc, independente de distribuição. Só muda se sua arquitetura for ARM (aarch64) em vez de x86_64.

O site da Oracle usa JavaScript e redirecionamentos com token temporário, portanto não é possível usar uma URL fixa diretamente. Siga os passos abaixo para obter a URL real:

  1. Acesse https://dev.mysql.com/downloads/mysql/
  2. Selecione: Innovation → 9.7.0 → Linux – Generic → x86_64
  3. Clique em Download e depois em "No thanks, just start my download"
  4. Copie o link real — terá o formato:
    https://cdn.mysql.com//Downloads/MySQL-9.7/mysql-9.7.0-linux-glibc2.28-x86_64.tar.xz

Com a URL em mãos, baixe com wget:

wget -c --show-progress \
  "https://cdn.mysql.com//Downloads/MySQL-9.7/mysql-9.7.0-linux-glibc2.28-x86_64.tar.xz" \
  -O /usr/local/src/mysql-9.7.0.tar.xz

Ou com curl:

curl -L --progress-bar \
  "https://cdn.mysql.com//Downloads/MySQL-9.7/mysql-9.7.0-linux-glibc2.28-x86_64.tar.xz" \
  -o /usr/local/src/mysql-9.7.0.tar.xz

Após o download, verifique a integridade (compare com o MD5/SHA256 exibido na página de download):

md5sum    /usr/local/src/mysql-9.7.0.tar.xz
sha256sum /usr/local/src/mysql-9.7.0.tar.xz

2. Dependências por Distribuição

Execute os comandos abaixo como root na sua distro antes de prosseguir.

Debian / Ubuntu

apt update
apt install -y libaio1t64 libncurses5 libssl3 libnuma1 xz-utils
# Em Debian mais antigo ou Ubuntu LTS sem libaio1t64:
apt install -y libaio1 || apt install -y libaio1t64

Arch Linux

pacman -Sy --noconfirm libaio ncurses openssl numactl xz

Fedora / RHEL / Rocky / AlmaLinux

dnf install -y libaio ncurses-libs openssl-libs numactl-libs xz
# Necessário no EL8 e EL9 para o cliente mysql funcionar:
dnf install -y ncurses-compat-libs

openSUSE Leap / Tumbleweed

zypper install -y libaio1 libncurses6 libopenssl3 libnuma1 xz

Nota openSUSE Leap antigo: substitua libncurses6 por libncurses5 se sua versão for anterior ao Leap 15.4.

3. Criar Usuário e Grupo mysql

Idêntico em todas as distros:

groupadd mysql
useradd -r -g mysql -s /bin/false -d /usr/local/mysql mysql

O flag -r cria uma conta de sistema (sem login interativo), e -s /bin/false impede qualquer acesso via shell.

4. Extrair e Posicionar os Binários

cd /usr/local

tar xJf /usr/local/src/mysql-9.7.0.tar.xz

# Cria o symlink canônico — facilita upgrades futuros
ln -s mysql-9.7.0-linux-glibc2.28-x86_64 mysql

cd mysql

# Diretório para importação/exportação segura (secure_file_priv)
mkdir mysql-files
chown mysql:mysql mysql-files
chmod 750 mysql-files

# Binários pertencem ao root; apenas os dados pertencem ao mysql
chown -R root .
chown -R mysql data

5. Arquivo de Configuração

Crie (ou edite) o arquivo /etc/my.cnf:

[mysqld]
basedir          = /usr/local/mysql
datadir          = /usr/local/mysql/data
socket           = /var/run/mysqld/mysqld.sock
pid-file         = /var/run/mysqld/mysqld.pid
log-error        = /var/log/mysql/error.log
secure-file-priv = /usr/local/mysql/mysql-files

# Ajuste conforme sua RAM (regra geral: 50–70% da RAM disponível)
innodb_buffer_pool_size = 512M

[client]
socket = /var/run/mysqld/mysqld.sock

Crie os diretórios de runtime e log:

mkdir -p /var/run/mysqld /var/log/mysql
chown mysql:mysql /var/run/mysqld /var/log/mysql

6. Inicializar o Data Directory

/usr/local/mysql/bin/mysqld \
  --initialize \
  --user=mysql \
  --basedir=/usr/local/mysql \
  --datadir=/usr/local/mysql/data

O MySQL gera uma senha temporária de root durante a inicialização. Anote-a:

grep 'temporary password' /var/log/mysql/error.log

Importante: se você perder essa senha, precisará reinicializar o data directory com --initialize novamente, o que apaga todos os dados.

7. Criar o Serviço systemd

Crie o arquivo /etc/systemd/system/mysql.service com o conteúdo abaixo. Este unit é compatível com todas as quatro distribuições:

[Unit]
Description=MySQL 9.7.0 Database Server
Documentation=https://dev.mysql.com/doc/refman/9.7/en/
After=network.target
After=syslog.target

[Service]
Type=notify

# Usuário e grupo
User=mysql
Group=mysql

# Inicialização
ExecStart=/usr/local/mysql/bin/mysqld \
    --basedir=/usr/local/mysql \
    --datadir=/usr/local/mysql/data \
    --socket=/var/run/mysqld/mysqld.sock \
    --pid-file=/var/run/mysqld/mysqld.pid \
    --log-error=/var/log/mysql/error.log

ExecReload=/bin/kill -HUP $MAINPID

# Garante criação automática do diretório /var/run/mysqld com permissão correta
RuntimeDirectory=mysqld
RuntimeDirectoryMode=0755

# Reinicia automaticamente em caso de falha inesperada
Restart=on-failure
RestartSec=5s

# Limites de recursos
LimitNOFILE=65535
LimitNPROC=65535
LimitCORE=0

# Timeout generoso para bancos grandes
TimeoutStartSec=900

[Install]
WantedBy=multi-user.target

8. Ativar e Iniciar o Serviço

# Recarrega o systemd para reconhecer o novo unit
systemctl daemon-reload

# Habilita o MySQL para iniciar junto com o sistema
systemctl enable mysql

# Inicia o serviço agora
systemctl start mysql

# Verifica o status
systemctl status mysql

Para acompanhar os logs em tempo real:

journalctl -u mysql -f

9. Adicionar os Binários ao PATH (Opcional)

Para usar comandos como mysql, mysqldump e mysqlcheck diretamente no terminal, sem informar o caminho completo:

echo 'export PATH=/usr/local/mysql/bin:$PATH' >> /etc/profile.d/mysql.sh
source /etc/profile.d/mysql.sh

10. Pós-instalação — Trocar a Senha e Proteger o Servidor

Conecte usando a senha temporária obtida no passo 6:

/usr/local/mysql/bin/mysql -u root -p

Dentro do prompt do MySQL, troque a senha imediatamente:

ALTER USER 'root'@'localhost' IDENTIFIED BY 'SuaSenhaForte!123';
FLUSH PRIVILEGES;

Alternativamente, use o assistente interativo que remove contas anônimas, desabilita acesso root remoto e remove o banco de teste:

/usr/local/mysql/bin/mysql_secure_installation

Resumo: Diferenças entre as Distribuições

Distribuição Detalhe específico
Debian 12+ / Ubuntu 24+ libaio1 foi renomeado para libaio1t64; verifique com apt search libaio
Arch Linux Pacotes com os nomes exatos: libaio, ncurses, numactl
Fedora / EL8 / EL9 Necessário instalar ncurses-compat-libs para o cliente mysql funcionar
openSUSE Tumbleweed usa libncurses6; Leap antigo usa libncurses5

O tarball, a estrutura de diretórios, o my.cnf e o unit systemd são idênticos nas quatro distribuições.

Referências

Exercícios

Exercício 1

Execute mentalmente o passo 4 na ordem escrita. O tarball genérico traz bin/, lib/, share/, support-files/ — e não traz um diretório data. O que acontece nas duas últimas linhas?

Ver resposta

✓ Resposta: As duas falham no objetivo, cada uma de um jeito.

chown -R mysql data imprime cannot access 'data': No such file or directory e sai com erro. O data só nasce no passo 6, criado pelo --initialize — que, aliás, já o cria com o dono certo, então essa linha aqui é inútil de qualquer forma.

A que morde de verdade é a anterior. chown -R root . é recursivo a partir de /usr/local/mysql e passa por cima do mysql-files que acabou de ser ajustado três linhas acima. Como chown root sem grupo preserva o grupo, o diretório termina assim:

drwxr-x---  root mysql  mysql-files/

Com 750, o grupo tem apenas r-x. O servidor roda como mysql, então ele desse diretório mas não escreve — e é justamente ele o secure-file-priv do my.cnf. O sintoma aparece muito depois da instalação, quando alguém tenta um SELECT ... INTO OUTFILE e recebe erro de permissão sem relação óbvia com a causa.

A ordem correta é a da documentação oficial: inicializar primeiro, ajustar donos depois, e incluir mysql-files junto com data:

bin/mysqld --initialize --user=mysql
chown -R root .
chown -R mysql data mysql-files

O padrão que isso ensina vale além do MySQL: chown -R desfaz, sem avisar, todo ajuste fino feito antes dele na mesma árvore. Permissão específica se aplica depois da recursiva, nunca antes.

Exercício 2

O passo 6 afirma: "se você perder essa senha, precisará reinicializar o data directory com --initialize novamente, o que apaga todos os dados". Avalie essa frase.

Ver resposta

✓ Resposta: É falsa, e perigosamente — alguém que acredite nela pode destruir um banco de produção para resolver um problema que se conserta em dois minutos.

A senha de root vive numa tabela do próprio banco (mysql.user). Perder a senha é perder o acesso, não os dados, e o MySQL tem um procedimento documentado para retomá-lo. O caminho recomendado é o --init-file, porque não abre o servidor sem autenticação em momento nenhum:

# 1. parar o serviço
systemctl stop mysql

# 2. arquivo com o comando de reset
printf "ALTER USER 'root'@'localhost' IDENTIFIED BY 'NovaSenhaForte!123';\n" \
  > /var/lib/mysql-reset.sql
chown mysql:mysql /var/lib/mysql-reset.sql

# 3. subir uma vez executando esse arquivo no boot
/usr/local/mysql/bin/mysqld --user=mysql --init-file=/var/lib/mysql-reset.sql

# 4. encerrar, apagar o arquivo e voltar ao normal
rm /var/lib/mysql-reset.sql
systemctl start mysql

A alternativa mais conhecida é --skip-grant-tables, e ela pede cuidado: nesse modo o servidor aceita qualquer conexão sem senha. Se for usada, sempre com --skip-networking junto, para restringir ao socket local.

Um detalhe que também desmonta a premissa da frase: o --initialize se recusa a rodar sobre um datadir que não esteja vazio. Ele não apagaria os dados — ele abortaria. Para chegar ao desastre descrito seria preciso apagar o diretório manualmente antes.

Exercício 3

O passo 1 manda rodar md5sum e sha256sum e comparar com o valor exibido na página de download. Contra que ameaça isso protege — e contra qual não protege?

Ver resposta

✓ Resposta: Protege contra corrupção: download interrompido, erro de disco, proxy que estragou o arquivo. Nesses casos o hash diverge e você repete o download.

Não protege contra adulteração, porque o arquivo e o hash de referência vêm da mesma origem. Quem consegue trocar o tarball no CDN consegue trocar o número na página ao lado — e você compara a cópia falsa com o hash da cópia falsa, obtendo um ✅ perfeito. O hash publicado junto do arquivo verifica integridade, não autenticidade.

O que fecha essa lacuna é assinatura. A Oracle publica um .asc para cada release, assinado com a chave GPG do MySQL Release Engineering:

# chave pública da Oracle/MySQL, obtida uma vez e por canal confiável
gpg --import mysql_pubkey.asc

wget https://cdn.mysql.com//Downloads/MySQL-9.7/mysql-9.7.0-linux-glibc2.28-x86_64.tar.xz.asc
gpg --verify mysql-9.7.0-linux-glibc2.28-x86_64.tar.xz.asc \
             /usr/local/src/mysql-9.7.0.tar.xz

A diferença é onde mora a confiança: no GPG ela está na chave que você já tem, não no servidor que serviu o arquivo agora. Falsificar a assinatura exige a chave privada da Oracle — comprometer o CDN não basta.

Vale ligar isso à premissa do artigo. A justificativa para o tarball genérico é fugir do que a distro empacota — mas é exatamente isso que o apt e o dnf fazem por você a cada instalação: verificam assinatura antes de instalar, com a chave do repositório já confiada. Ao sair do gerenciador de pacotes, essa verificação passa a ser sua. E o md5sum, ainda por cima, usa um algoritmo em que colisões são produzidas há anos.

Exercício 4

O passo 5 cria /var/run/mysqld com mkdir -p, e o unit do passo 7 declara RuntimeDirectory=mysqld. Parece redundante. Reinicie a máquina e diga o que sobrou de cada um.

Ver resposta

✓ Resposta: Do mkdir não sobra nada. Do RuntimeDirectory sobra o serviço funcionando — e é ele que faz o trabalho de verdade.

/var/run é um link para /run, que é tmpfs: existe só em memória e é recriado vazio a cada boot. O diretório criado à mão serve para a instalação de hoje e desaparece no próximo reinício. Sem o RuntimeDirectory, o MySQL falharia ao subir no boot seguinte por não conseguir criar o socket — uma falha que só aparece no primeiro reboot, tipicamente semanas depois, quando ninguém associa mais ao dia da instalação.

O que a diretiva do systemd faz, e o mkdir não faz:

  • cria /run/mysqld a cada start, antes do ExecStart;
  • aplica dono e grupo do User=/Group= automaticamente, e o modo do RuntimeDirectoryMode;
  • remove o diretório quando o serviço para, junto com socket e pid órfãos.

Esse último ponto merece atenção porque tem um efeito colateral: enquanto o serviço está parado, /run/mysqld não existe. Uma ferramenta que aponte para o socket via [client] socket = /var/run/mysqld/mysqld.sock reclama de caminho inexistente em vez de "conexão recusada" — mensagem diferente, mesma causa.

O mkdir manual não é totalmente inútil: ele permite rodar o --initialize do passo 6, que acontece antes de o serviço existir. O erro seria acreditar que ele basta. Já /var/log/mysql, criado na mesma linha, é persistente — esse sim precisa do mkdir.

Exercício 5

O unit usa Type=notify com TimeoutStartSec=900. Um serviço de aplicação declara After=mysql.service e Requires=mysql.service. O que muda para essa aplicação se o tipo fosse Type=simple?

Ver resposta

✓ Resposta: Com Type=simple, a aplicação sobe antes de o MySQL aceitar conexões e falha no boot com "connection refused".

A diferença está em quando o systemd considera o serviço iniciado:

  • Type=simple: no instante em que consegue executar o binário. O processo existe — mas o MySQL ainda vai abrir tablespaces, aplicar o redo log e recuperar transações pendentes. Em base grande isso leva minutos.
  • Type=notify: quando o próprio mysqld avisa pelo sd_notify que terminou de inicializar e está pronto. O After= da aplicação passa a significar o que se espera dele.

É por isso que o TimeoutStartSec=900 só faz sentido acompanhado de Type=notify. Com simple ele não teria o que medir — não há evento de "pronto" a esperar. Com notify, ele é a folga que impede o systemd de declarar falha e matar um servidor que está legitimamente recuperando um banco grande. Reduzi-lo para o padrão de 90 segundos produz um modo de falha cruel: o systemd mata o mysqld no meio da recuperação, o Restart=on-failure o sobe de novo, a recuperação recomeça do zero, e o ciclo se repete.

Uma ressalva que evita a conclusão fácil: notify garante que o processo terminou de inicializar, não que o banco esteja em estado utilizável para a aplicação — migrações pendentes, réplica atrasada e permissões faltando continuam sendo problema dela. Ordenar no systemd elimina a corrida do boot; não substitui retry com espera na própria aplicação, que é o que a protege quando o banco cai depois, com tudo já em pé.

Comentários

Mais em DevOps

AIOps na Prática: O Que a IA Já Faz em Operações Hoje
AIOps na Prática: O Que a IA Já Faz em Operações Hoje

Avaliação honesta do AIOps em quatro faixas de maturidade: detecção de…

Boas Práticas de Imagens: Leveza, Segurança e Camadas
Boas Práticas de Imagens: Leveza, Segurança e Camadas

Os princípios que separam uma imagem funcional de uma imagem de produção…

Navegando pelo Sistema de Arquivos com Confiança
Navegando pelo Sistema de Arquivos com Confiança

A árvore de diretórios do Linux e o que cada ramo guarda: /etc, /var, /home…