Fazer backup diário das bases de dados MySQL no Debian, Ubuntu e Linux
Um pequeno script mais um cronjob chegam para guardar todas as noites, de forma comprimida, todas as bases de dados MySQL e MariaDB. Incluindo os pontos em que Debian e Ubuntu divergem e um backup falha em silêncio.
Tem Debian, Ubuntu ou outra distribuição Linux a correr no seu vServer, servidor root ou servidor dedicado e quer fazer backup automático diário de todas as suas bases de dados MySQL e MariaDB? Então está no sítio certo. Neste guia vai configurar um script de backup que, todas as noites, exporta todas as bases de dados para um ficheiro SQL comprimido e limpa automaticamente as cópias antigas.
O procedimento é o mesmo em todas as distribuições e funciona tanto no Debian e no Ubuntu como no AlmaLinux, no Rocky Linux e no RHEL. As diferenças não estão no script, mas sim no servidor de base de dados que tem instalado e na forma como este permite a autenticação. Para isso existe mais abaixo uma secção própria sobre Debian em comparação com Ubuntu.
Requisitos
Precisa de um acesso SSH com privilégios de root e de um servidor MySQL ou MariaDB já instalado. Recomendamos as versões atuais de cada distribuição: Debian 13 "Trixie" e Debian 12 "Bookworm", Ubuntu 24.04 LTS "Noble Numbat" e Ubuntu 22.04 LTS "Jammy Jellyfish", bem como AlmaLinux e Rocky Linux nas versões 9 e 10. Já não deve usar sistemas mais antigos em produção: o Debian 10 chegou ao fim do suporte em junho de 2024, o Ubuntu 20.04 LTS em maio de 2025, e o CentOS 7 também já não recebe atualizações de segurança regulares.
Comece por atualizar o sistema e instale o editor de texto Nano, caso ainda não esteja presente.
No Debian e no Ubuntu:
apt update && apt upgrade -y
apt install nano -y
No AlmaLinux, no Rocky Linux e no RHEL:
dnf update -y
dnf install nano -y
Nos sistemas atuais da família RHEL, o dnf é o sucessor do yum. O comando antigo continua a funcionar na maioria dos casos como ligação, mas o que deve usar é o dnf.
Conte também com espaço de armazenamento suficiente. Uma semana de backups comprimidos ocupa rapidamente vários gigabytes, consoante o tamanho das bases de dados. O espaço livre verifica-se com df -h.
Guardar as credenciais em segurança
A palavra-passe não pertence diretamente à chamada do mysqldump, porque as linhas de comando ficam visíveis para todos os utilizadores do sistema através do ps. Crie antes um ficheiro de credenciais que só o root possa ler:
nano /root/.my.cnf
Conteúdo do ficheiro:
[client]
user=root
password=A_SUA_PALAVRA_PASSE_DA_BASE_DE_DADOS
Depois defina as permissões de forma a que apenas o root tenha acesso:
chmod 600 /root/.my.cnf
Quando não existe qualquer palavra-passe de root
No Debian e no Ubuntu, a conta de base de dados root normalmente não está protegida por uma palavra-passe, mas sim pela identidade do utilizador do sistema. O MariaDB chama a esse método unix_socket, o MySQL chama-lhe auth_socket. Nesse caso não existe simplesmente palavra-passe nenhuma, e uma entrada no ficheiro de credenciais não leva a lado nenhum. Esta consulta mostra se é esse o seu caso:
mysql -e "SELECT user, host, plugin FROM mysql.user WHERE user='root';"
Se aparecer unix_socket ou auth_socket, tem duas hipóteses: ou executa o script como utilizador de sistema root e dispensa por completo o ficheiro de credenciais, ou cria um utilizador de backup próprio. A segunda variante é a melhor, porque não precisa dos privilégios totais da conta root:
CREATE USER 'kh_backup'@'localhost' IDENTIFIED BY 'A_SUA_PALAVRA_PASSE_DE_BACKUP';
GRANT SELECT, SHOW VIEW, EVENT, TRIGGER, LOCK TABLES, RELOAD, PROCESS ON *.* TO 'kh_backup'@'localhost';
FLUSH PRIVILEGES;
No ficheiro de credenciais indica então user=kh_backup. Os privilégios foram deliberadamente reduzidos ao mínimo: leitura, vistas, eventos, triggers e, como último recurso, o bloqueio de tabelas que não usem InnoDB. RELOAD e PROCESS só podem ser atribuídos globalmente, daí o ON *.*. O MySQL 8 precisa de PROCESS para ler também as informações de tablespace. Quem não quiser atribuí-lo acrescenta antes --no-tablespaces no script. Se já não conseguir mesmo entrar, o artigo Repor a palavra-passe de root do MySQL e do MariaDB ajuda.
Criar o script de backup
Vamos agora criar um script Bash que trata da exportação e apaga os backups antigos. No nosso exemplo chama-se mysql_export_all.sh e fica no diretório /opt/mysqlbackups:
mkdir -p /opt/mysqlbackups
nano /opt/mysqlbackups/mysql_export_all.sh
Escreva neste script o seguinte conteúdo:
#!/bin/bash
set -euo pipefail
BACKUP_DIR="/opt/mysqlbackups"
KEEP_DAYS=7
DATE=$(date +%Y-%m-%d-%H-%M)
mkdir -p "$BACKUP_DIR"
mysqldump --defaults-extra-file=/root/.my.cnf --all-databases --single-transaction --routines --events | gzip > "$BACKUP_DIR/alldbs_$DATE.sql.gz"
find "$BACKUP_DIR" -type f -name "alldbs_*.sql.gz" -mtime +$KEEP_DAYS -delete
Resumo das opções mais importantes:
--defaults-extra-filelê o utilizador e a palavra-passe do ficheiro que acabou de criar. Esta opção tem de ser a primeira da lista.--single-transactioncria um estado consistente em si mesmo nas tabelas InnoDB, sem bloquear a base de dados.--routinese--eventsincluem no backup os procedimentos armazenados e os eventos agendados. Os triggers, esses, omysqldumpguarda por iniciativa própria.- O
gzipcomprime a exportação e poupa assim bastante espaço de armazenamento. - O comando
findapaga exclusivamente os ficheiros de backup com mais de sete dias. Através deKEEP_DAYSajusta o período de retenção.
A linha set -euo pipefail é mais importante do que parece. Sem pipefail, a shell avalia apenas o código de saída do gzip, e esse é zero mesmo quando o mysqldump foi interrompido antes. Ficaria com um ficheiro de arquivo tecnicamente correto mas de conteúdo incompleto, e ninguém daria por isso.
O servidor de base de dados que tem em execução depende da distribuição. O Debian não inclui qualquer pacote mysql-server e aposta sempre no MariaDB, ao passo que o Ubuntu oferece as duas opções. O script funciona sem alterações em todos os casos:
| Sistema | mysql-server | mariadb-server |
|---|---|---|
| Debian 13 | não disponível | 11.8 |
| Debian 12 | não disponível | 10.11 |
| Ubuntu 24.04 LTS | 8.0 | 10.11 |
| Ubuntu 22.04 LTS | 8.0 | 10.6 |
Tornar o script executável e testá-lo
Torne o script executável:
chmod +x /opt/mysqlbackups/mysql_export_all.sh
Execute-o uma vez à mão e verifique o resultado antes de o automatizar:
/opt/mysqlbackups/mysql_export_all.sh
ls -lh /opt/mysqlbackups/
O ficheiro gerado deve ser claramente maior do que zero bytes. Mais revelador do que o início é, no entanto, o fim. Verifique por isso se o arquivo está intacto e se a exportação chegou mesmo ao fim:
gzip -t /opt/mysqlbackups/alldbs_*.sql.gz
zcat /opt/mysqlbackups/alldbs_*.sql.gz | tail -n 1
A última linha de uma exportação completa começa por -- Dump completed on. Se faltar, a exportação foi interrompida e o ficheiro não vale nada como cópia de segurança, mesmo que ocupe várias centenas de megabytes. Esta única linha é o teste fiável mais rápido de que dispõe.
Configurar o cronjob para o backup diário
Abra o editor de cronjobs:
export VISUAL=nano; crontab -e
Para um backup diário às 5 da manhã acrescente a seguinte linha:
0 5 * * * /opt/mysqlbackups/mysql_export_all.sh >> /var/log/mysql-backup.log 2>&1
Desta forma o resultado vai para um ficheiro de log, o que lhe permite analisar erros mais tarde. Para confirmar se o cronjob ficou corretamente registado use crontab -l. A tarefa tem de estar na crontab do root. Em muitas imagens do Ubuntu o início de sessão como root está desativado e aí o comando é sudo crontab -e. Caso contrário, como utilizador normal cria uma crontab própria, e o script acaba mais tarde por falhar no ficheiro de credenciais em /root.
Em instalações mínimas da família RHEL falta por vezes o serviço de cron. Instala-se e ativa-se assim:
dnf install cronie -y
systemctl enable --now crond
A partir de agora todas as bases de dados são exportadas todas as noites às 5 horas, e os backups com mais de sete dias desaparecem automaticamente. Mais sobre agendamentos e armadilhas típicas encontra em Configurar um cronjob no Linux.
Restaurar um backup
Um backup só vale alguma coisa quando também o consegue repor. Teste por isso o restauro uma vez de forma deliberada, de preferência num sistema de testes:
zcat /opt/mysqlbackups/alldbs_2026-07-26-05-00.sql.gz | mysql --defaults-extra-file=/root/.my.cnf
Se quiser recuperar apenas uma base de dados, comece por descomprimir o backup e extraia a secção correspondente, ou faça adicionalmente cópias separadas de cada base de dados com mysqldump --databases meinedb. Mais prático é escrever logo um ficheiro próprio por base de dados. Para isso substitua no script a linha do mysqldump por este ciclo:
for DB in $(mysql --defaults-extra-file=/root/.my.cnf -N -B -e "SHOW DATABASES;" | grep -Ev '^(information_schema|performance_schema|sys)$'); do
mysqldump --defaults-extra-file=/root/.my.cnf --single-transaction --routines --events "$DB" | gzip > "$BACKUP_DIR/${DB}_$DATE.sql.gz"
done
As três bases de dados excluídas são vistas sobre estados internos do servidor e não podem ser restauradas de forma útil. Ajuste também o padrão de pesquisa no comando find, caso contrário deixa de limpar os novos nomes de ficheiro. Para uma mudança completa para outro servidor, Migrar o WordPress para um novo servidor mostra o procedimento num exemplo prático.
Guardar os backups fora do servidor
As cópias de segurança que ficam apenas no mesmo servidor não ajudam em nada quando se perdem os dados de todo o sistema. Transfira por isso os ficheiros adicionalmente para um segundo destino, por exemplo com rsync ou scp para outro servidor ou para um armazenamento de backup:
rsync -avz /opt/mysqlbackups/ utilizador@destino-backup:/caminho/para/backup/
Basta acrescentar este comando no fim do seu script para que a transferência passe a correr automaticamente. Para que isso funcione no cronjob sem perguntas, o root precisa de uma chave SSH sem passphrase cuja parte pública esteja registada no sistema de destino. Como criá-la está descrito em Ligar ao servidor por SSH.
Diferenças entre Debian e Ubuntu
Os dois sistemas usam o apt, ambos guardam a configuração em /etc/mysql/, e o script acima corre em ambos sem alterações. Ainda assim há seis pontos em que divergem, e qualquer um deles pode fazer um backup falhar em silêncio:
| Tema | Debian | Ubuntu |
|---|---|---|
| Servidor de base de dados | exclusivamente MariaDB | MySQL 8.0 ou MariaDB |
| Pacote de cliente | mariadb-client | mysql-client-8.0 ou mariadb-client |
| Ferramenta de backup | mariadb-dump, mysqldump como ligação | no MySQL apenas mysqldump |
| Autenticação do root | unix_socket | auth_socket no MySQL do pacote |
| Conta de manutenção | deixou de ser criada a partir do MariaDB 10.4 | debian-sys-maint em /etc/mysql/debian.cnf |
| Log de erros | journal, journalctl -u mariadb | no MySQL /var/log/mysql/error.log |
Nomes de pacotes e ferramentas
Se fizer o backup da base de dados a partir de outra máquina, aí só precisa do pacote de cliente. No Debian chama-se mariadb-client, no Ubuntu com MySQL 8 chama-se mysql-client-8.0. Para um script que deva correr nos dois sistemas existe o metapacote default-mysql-client: no Debian aponta para o cliente do MariaDB e no Ubuntu para o cliente do MySQL.
A partir do MariaDB 10.5 as ferramentas passaram a ter também um nome com o prefixo mariadb-, e a partir do MariaDB 11 esse é o nome verdadeiro, sendo mysqldump apenas uma ligação para ele. Aí funcionam as duas chamadas. Já num sistema Ubuntu com MySQL 8 existe exclusivamente mysqldump, e um script com mariadb-dump termina com command not found. Para scripts que devam correr nos dois mundos, mysqldump é por isso a chamada certa.
Conta de manutenção e autenticação
No Ubuntu, o pacote mysql-server continua a criar a conta de manutenção debian-sys-maint e escreve as respetivas credenciais em /etc/mysql/debian.cnf. Este ficheiro só é legível pelo root e pode ser usado diretamente para um backup, sem qualquer preparação adicional:
mysqldump --defaults-file=/etc/mysql/debian.cnf --all-databases --single-transaction --routines --events | gzip > /opt/mysqlbackups/alldbs.sql.gz
No Debian com MariaDB a partir da versão 10.4 esta conta deixou de ser criada de novo. O ficheiro normalmente ainda existe, mas em regra aponta apenas para o root através do socket. Não parta portanto do princípio de que um script que no Ubuntu funciona com este ficheiro faz o mesmo no Debian. Pode confirmá-lo num único passo:
mysql --defaults-file=/etc/mysql/debian.cnf -e "SELECT current_user();"
O MySQL 8 cria as contas novas com o método caching_sha2_password, o MariaDB com mysql_native_password. Para a exportação através do socket local isso não tem importância, mas tem quando faz o backup pela rede a partir de um cliente mais antigo. Se este devolver "The server requested authentication method unknown to the client", o cliente é demasiado antigo para o MySQL 8 e deve ser atualizado.
AppArmor no Ubuntu
No Ubuntu o AppArmor está ativo de origem e restringe o servidor de base de dados, ou seja, o processo mysqld ou mariadbd. Não restringe a ferramenta mysqldump, e é esta diferença que decide se vai ou não dar de caras com a armadilha.
A exportação descrita neste guia escreve o ficheiro através da shell (| gzip > ...), ou seja, com o utilizador que executa o script. Este caminho não é afetado pelo AppArmor e funciona em qualquer diretório onde o utilizador possa escrever. Assim que é o próprio servidor a escrever, porém, aplicam-se outras regras. É o que acontece com mysqldump --tab=/pfad e com qualquer SELECT ... INTO OUTFILE, porque nesse caso é o processo do servidor que cria o ficheiro, e não a sua shell. Entram então em ação dois bloqueios independentes um do outro: a variável do servidor secure_file_priv e o perfil AppArmor do servidor. No Ubuntu a variável aponta de origem para /var/lib/mysql-files/, e é precisamente esse o diretório autorizado no perfil AppArmor. Um caminho de destino como /opt/mysqlbackups falha por isso duas vezes.
O que é traiçoeiro nisto é o diagnóstico. O primeiro bloqueio anuncia-se claramente com ERROR 1290 (HY000): The MySQL server is running with the --secure-file-priv option so it cannot execute this statement. O segundo aparece, pelo contrário, como um simples Errcode: 13 "Permission denied", apesar de o dono e as permissões do diretório de destino estarem, à primeira vista, perfeitamente em ordem. Quem se põe então a atribuir permissões de ficheiro está a procurar no sítio errado. Verifique nesse caso as duas coisas:
mysql -e "SHOW VARIABLES LIKE 'secure_file_priv';"
aa-status | grep -Ei 'mysqld|mariadbd'
journalctl -k | grep -i 'apparmor.*DENIED' | tail -n 20
Se no último comando aparecer uma linha com apparmor="DENIED" e o seu caminho de destino, encontrou a causa. O mais simples é nem sequer deixar chegar a esse ponto e manter a exportação através da pipe. Se precisar mesmo de escritas do lado do servidor, escreva para /var/lib/mysql-files/ e mova o ficheiro em seguida. Só quando nenhuma das duas vias for possível é que deve alargar o perfil com o caminho adicional. Para isso está previsto o ficheiro /etc/apparmor.d/local/usr.sbin.mysqld, no MariaDB o correspondente usr.sbin.mariadbd, seguido de systemctl reload apparmor. Desligar o AppArmor não é uma solução, é retirar uma camada de proteção que protege exatamente este servidor.
Erros frequentes e respetivas soluções
O backup tem 0 bytes: Na maioria dos casos as credenciais em /root/.my.cnf não estão corretas. Teste a autenticação com mysql --defaults-extra-file=/root/.my.cnf -e "SHOW DATABASES;". Muitas vezes está por trás o caso descrito acima, em que a conta trabalha através do socket e uma palavra-passe guardada não encaixa de todo.
"Access denied" no cron, mas não na consola: O cronjob corre com um utilizador diferente do esperado. Registe a tarefa na crontab do root e use no script apenas caminhos absolutos. Outras causas estão reunidas em Resolver o erro MySQL Access denied for user.
Unknown table 'COLUMN_STATISTICS' in information_schema: Está a fazer o backup de uma base de dados MariaDB com o mysqldump do MySQL 8, por exemplo a partir de uma máquina Ubuntu. Na exportação, o MySQL 8 consulta uma tabela que não existe no MariaDB. Acrescente --column-statistics=0 à chamada. A opção só é conhecida pelo cliente do MySQL, num sistema exclusivamente MariaDB não a pode definir.
Access denied; you need (at least one of) the PROCESS privilege(s) for this operation: No MySQL 8 o utilizador de backup não pode ler as informações de tablespace. Atribua PROCESS como descrito acima ou acrescente --no-tablespaces.
mariadb-dump: command not found: O script vem de um sistema Debian com MariaDB 11 e está agora a correr no Ubuntu com MySQL 8. Aí esse nome não existe. Escreva mysqldump, que funciona nos dois sistemas.
Can't connect to local MySQL server through socket: O servidor não está a correr, ou o socket encontra-se num caminho diferente do esperado. As causas possíveis são descritas em Resolver erros de socket do MySQL.
O SELinux bloqueia o script (AlmaLinux, Rocky Linux, RHEL): Verifique com ausearch -m avc -ts recent se ficou registada alguma negação de acesso e ajuste, se necessário, o contexto do diretório de backup.
O disco enche-se: Reduza o KEEP_DAYS ou mova os backups mais antigos para um armazenamento externo. O que mais ocupa espaço é mostrado em Libertar espaço com o disco cheio no Linux.
Mensagem de erro sobre --single-transaction em tabelas MyISAM: Esta opção só produz efeito com InnoDB. Em tabelas MyISAM pode usar antes --lock-tables, e nesse caso as tabelas ficam bloqueadas durante a exportação. Tenha ainda em conta que no MariaDB as tabelas de sistema estão no motor Aria e não são abrangidas por --single-transaction.
Se já está a mexer na base de dados, aproveite para tratar do resto: remover utilizadores anónimos, apagar a base de dados de teste e desativar o acesso remoto do root. Como se faz está em Proteger o MariaDB e o MySQL.
Perguntas frequentes
Porque é que a palavra-passe da base de dados não deve estar no comando mysqldump?
O que distingue o Debian do Ubuntu no backup de bases de dados MySQL?
No Ubuntu o AppArmor impede a escrita do ficheiro de backup. O que fazer?
O guia funciona em qualquer distribuição Linux?
O que faço se a conta de base de dados root não tiver palavra-passe nenhuma?
Durante quanto tempo são guardados os backups?
Basta guardar os backups no mesmo servidor?
Como reponho um backup?
O cronjob não corre ou devolve Access denied. O que posso verificar?
O backup tem 0 bytes. A que se deve?
2024-2026 KernelHost GmbH. Todos os direitos reservados. Este guia está protegido por direitos de autor. A sua republicação noutros sites, na íntegra, em parte ou de forma editada, não é permitida sem o nosso consentimento por escrito. Citações com indicação da fonte e ligação são expressamente bem-vindas.

