Como instalar o MinIO em um VPS Ubuntu 24.04
Configure o MinIO em um VPS Ubuntu 24.04 com binário verificado, systemd, comandos mc, URLs preassinadas e um destino S3 para backups do restic.
O que o armazenamento de objetos auto-hospedado com MinIO oferece
O MinIO é um armazenamento de objetos auto-hospedado compatível com a API Amazon S3. Aponte o restic ou qualquer SDK S3 para seu próprio servidor, altere uma configuração de endpoint e o cliente não perceberá a diferença. Este guia configura um único nó no Ubuntu 24.04: um binário verificado, um usuário de sistema dedicado, uma unidade systemd que mantém as credenciais root fora do arquivo da unidade e um bucket no qual o restic armazena os backups.
S3 (serviço de armazenamento simples) é uma API HTTP, não um sistema de arquivos. Você usa PUT para gravar um objeto em um bucket sob uma chave e GET para recuperá-lo. Não há gravação parcial nem renomeação. As ferramentas de backup usam esse modelo porque um objeto foi recebido inteiro ou não foi recebido.
Um nó mantém uma única cópia dos seus dados. Essa é a troca que você está fazendo. Você obtém um endpoint S3 sob seu controle pelo preço de um VPS e também assume todas as tarefas que o provedor de nuvem executava, desde substituir um disco com falha até aplicar patches no software do servidor. A seção próxima ao final explica claramente quando essa troca é adequada.
Estado da edição comunitária do MinIO em julho de 2026
Leia esta parte antes de usá-la como base, porque ela mudou recentemente. Em maio de 2025, o MinIO removeu os recursos de administração do console web na edição comunitária. O que resta no navegador é um navegador de objetos. Portanto, buckets e chaves de acesso são gerenciados com o cliente de linha de comando mc.
Mais tarde, em 2025, o MinIO deixou de publicar binários comunitários pré-compilados. O README do projeto agora informa que a edição comunitária é distribuída apenas como código-fonte. As URLs de download antigas ainda funcionam: em julho de 2026, elas disponibilizam o build do servidor RELEASE.2025-09-07T16-13-09Z e o build do cliente RELEASE.2025-08-13T08-35-41Z, e nenhum build comunitário mais recente foi publicado. Portanto, o binário abaixo é real, funciona e está congelado. As correções de segurança publicadas depois de setembro de 2025 não estão incluídas nele.
Esse fato orienta o restante deste guia. É por isso que o MinIO escuta em 127.0.0.1 e acessa a internet somente por meio de um proxy que você controla. Se preferir acompanhar as correções, compile a partir do código-fonte. O README do fornecedor fornece um único comando, go install github.com/minio/minio@latest, que requer uma toolchain Go e grava o binário em ~/go/bin/minio. Instale esse binário em /usr/local/bin/minio, e todas as outras etapas deste guia permanecem inalteradas.
Instale o binário do MinIO e verifique o download
Baixe a versão fixada e o checksum publicado. A flag -f faz curl falhar em caso de erro HTTP, em vez de salvar a página de erro com o nome solicitado. Caso contrário, você pode acabar instalando uma página 404 e se perguntar por que ela não é executada.
cd /tmp
REL=RELEASE.2025-09-07T16-13-09Z
curl -fsSL "https://dl.min.io/server/minio/release/linux-amd64/archive/minio.$REL" -o minio
curl -fsSL "https://dl.min.io/server/minio/release/linux-amd64/archive/minio.$REL.sha256sum" -o minio.sha256sumCompare os dois hashes e compare somente os hashes.
published=$(awk '{print $1}' minio.sha256sum)
downloaded=$(sha256sum minio | awk '{print $1}')
[ "$published" = "$downloaded" ] && echo "checksum ok"Não use sha256sum -c minio.sha256sum aqui. O rótulo gravado depois do hash nesse arquivo é minio.RELEASE.2025-09-07T16-13-09Z, e salvamos o download como minio. Por isso, -c procura um arquivo que não existe. Ele informa No such file or directory e depois WARNING: 1 listed file could not be read. Isso parece um download corrompido, mas não é. O rótulo é apenas um nome. O hash é a parte que fornece a garantia.
É importante entender o que essa verificação comprova. O binário e o hash vêm do mesmo fornecedor pela mesma conexão. Portanto, uma correspondência comprova que o download está completo e não foi danificado nem alterado durante o trânsito. Ela não comprova que o fornecedor é confiável. Esse é outro problema, e nenhum comando sha256sum o resolve.
sudo install -o root -g root -m 755 minio /usr/local/bin/minio
minio --versionminio --version imprime minio version RELEASE.2025-09-07T16-13-09Z seguido de algumas linhas de compilação. Um Permission denied nesse caso indica que o modo está incorreto, e command not found significa que /usr/local/bin não está no seu PATH.
Criar um usuário do sistema e um diretório de dados
O MinIO aceita uploads pela rede, portanto não deve ser executado como root. Dê a ele uma conta sem diretório inicial e sem shell de login.
sudo groupadd -r minio-user
sudo useradd -M -r -g minio-user -s /usr/sbin/nologin minio-user
sudo mkdir -p /var/lib/minio/data
sudo chown -R minio-user:minio-user /var/lib/minio
sudo chmod 750 /var/lib/minio-r cria uma conta do sistema com um UID abaixo de 1000, mantendo-a fora do intervalo usado por pessoas. -M não cria o diretório inicial, porque uma conta que nunca faz login não tem nada para manter nele. Verifique o resultado com id minio-user e com stat -c '%U %a' /var/lib/minio, que deve exibir minio-user 750.
O diretório de dados precisa permitir gravação por esse usuário, não apenas leitura. Na primeira inicialização, o MinIO cria um diretório .minio.sys dentro do volume para armazenar a própria configuração. Por isso, um diretório pertencente a root faz o MinIO sair durante a inicialização com uma mensagem que termina em permission denied. A mesma regra se aplica a todos os serviços executados dessa forma, e usuários de serviço com privilégio mínimo em um VPS explica isso corretamente.
Coloque as credenciais do root em um arquivo de ambiente
As credenciais do root dão acesso a todos os buckets. Portanto, elas não devem ficar no arquivo da unit, que pode ser lido por qualquer usuário. Crie primeiro o arquivo com o modo correto e grave nele depois. Assim, a senha nunca fica em um arquivo legível, nem por um instante.
sudo install -o root -g root -m 600 /dev/null /etc/default/minio
printf 'MINIO_ROOT_USER=minio-root\nMINIO_ROOT_PASSWORD=%s\nMINIO_VOLUMES="/var/lib/minio/data"\nMINIO_OPTS="--address 127.0.0.1:9000 --console-address 127.0.0.1:9001"\n' "$(openssl rand -base64 24)" | sudo tee /etc/default/minio > /dev/null
sudo sed -n 's/^MINIO_ROOT_PASSWORD=//p' /etc/default/miniotee trunca um arquivo existente em vez de recriá-lo. Assim, o modo permanece em 600 e o proprietário continua sendo root. Isso é intencional. O systemd lê EnvironmentFile como root antes de abandonar os privilégios para usar User=. Portanto, a conta de serviço nunca precisa ler as próprias credenciais. Depois que o serviço estiver em execução, confirme isso com sudo -u minio-user cat /etc/default/minio. Esse comando deve imprimir Permission denied.
É importante conhecer dois comportamentos do MinIO antes de iniciá-lo. Sem MINIO_ROOT_USER e sem MINIO_ROOT_PASSWORD no ambiente, o MinIO não recusa a inicialização. Ele inicia com as credenciais padrão documentadas, minioadmin:minioadmin. Esse é o primeiro par que qualquer scanner tenta, e o serviço parece estar totalmente saudável mesmo nessa situação. Uma senha com menos de 8 caracteres é rejeitada. Nesse caso, o MinIO encerra a inicialização com um erro informando que as credenciais são inválidas, porque a access key precisa ter pelo menos 3 caracteres e a secret key, pelo menos 8.
MINIO_VOLUMES é o caminho dos dados e MINIO_OPTS contém as flags. Fazer o bind em 127.0.0.1 significa que nada fora desta VPS pode acessar a API S3 ainda. Esse é o padrão correto. Mais tarde, você a abrirá deliberadamente por meio de um proxy que terá um certificado.
Escreva a unidade systemd
Crie /etc/systemd/system/minio.service:
[Unit]
Description=MinIO object storage
Documentation=https://github.com/minio/minio
Wants=network-online.target
After=network-online.target
[Service]
User=minio-user
Group=minio-user
EnvironmentFile=/etc/default/minio
ExecStart=/usr/local/bin/minio server $MINIO_VOLUMES $MINIO_OPTS
Restart=always
RestartSec=5
LimitNOFILE=65536
NoNewPrivileges=true
[Install]
WantedBy=multi-user.targetNão há um - inicial em EnvironmentFile. Isso é uma decisão, não um erro de digitação. Com o hífen, o systemd ignora um arquivo ausente e inicia o MinIO mesmo assim. Dessa forma, um arquivo excluído ou um caminho digitado incorretamente entrega silenciosamente um servidor em execução com minioadmin:minioadmin. Sem o hífen, um arquivo ausente faz a unidade falhar antes que o MinIO seja executado, e journalctl -u minio exibe Failed to load environment files: No such file or directory. Uma unidade que se recusa a iniciar é muito mais fácil de perceber do que um servidor que aceita silenciosamente a senha padrão.
$MINIO_VOLUMES e $MINIO_OPTS não usam aspas de propósito, porque o systemd divide variáveis sem aspas nos espaços em branco, transformando-as em argumentos separados. É assim que as quatro palavras em MINIO_OPTS se tornam quatro argumentos para minio server. LimitNOFILE=65536 aumenta o limite de descritores de arquivo, porque cada conexão aberta e cada arquivo de dados aberto consome um descritor, e o limite padrão de 1024 se esgota sob carga.
sudo systemctl daemon-reload
sudo systemctl enable --now minio
systemctl is-active minio
curl -fsS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:9000/minio/health/liveis-active deve exibir active, e o endpoint de integridade deve responder 200. journalctl -u minio -n 20 --no-pager exibe o endereço da API em que o servidor está escutando. Se a unidade continuar reiniciando, o systemd desistirá e registrará Start request repeated too quickly. Isso significa que o MinIO sai em todas as tentativas. O motivo aparece nas linhas acima dessa mensagem, portanto leia para cima.
Para obter mais isolamento, adicione ProtectSystem=full e ProtectHome=true à seção [Service]. Ambos precisam de namespaces de montagem fornecidos pelo kernel do host. Em virtualização de contêineres que compartilha o kernel do host, como OpenVZ ou LXC, eles podem falhar, e a unidade informará status=226/NAMESPACE. Remova essas duas linhas para que ela seja iniciada. A unidade em si é uma unidade comum, e serviços e timers do systemd em um VPS aborda o restante das diretivas.
Instalar mc e comprovar uma ida e volta
O cliente MinIO é mc. Não o instale com apt install mc. Esse pacote é o Midnight Commander, um gerenciador de arquivos sem relação com o MinIO.
cd /tmp
curl -fsSL https://dl.min.io/client/mc/release/linux-amd64/mc -o mc
curl -fsSL https://dl.min.io/client/mc/release/linux-amd64/mc.sha256sum -o mc.sha256sum
[ "$(awk '{print $1}' mc.sha256sum)" = "$(sha256sum mc | awk '{print $1}')" ] && echo "checksum ok"
sudo install -o root -g root -m 755 mc /usr/local/bin/mcRegistre o servidor como um alias e mova um objeto por ele.
MINIO_PASS=$(sudo sed -n 's/^MINIO_ROOT_PASSWORD=//p' /etc/default/minio)
mc alias set local http://127.0.0.1:9000 minio-root "$MINIO_PASS"
mc mb local/backups
echo "hello object storage" > /tmp/hello.txt
mc cp /tmp/hello.txt local/backups/hello.txt
mc ls local/backups
mc cat local/backups/hello.txtmc ls deve listar hello.txt com seu tamanho, e mc cat deve imprimir hello object storage. Essa ida e volta é a comprovação real de que o servidor funciona, porque ela faz as mesmas solicitações S3 assinadas que qualquer outro cliente fará. mc admin info local exibe o status do servidor, caso você queira uma segunda confirmação.
Execute agora mais uma verificação, enquanto o servidor ainda está vazio.
mc alias set defaultcheck http://127.0.0.1:9000 minioadmin minioadminEsse comando deve falhar. Se for bem-sucedido, o arquivo de ambiente nunca chegou ao processo, e o servidor está usando as credenciais padrão. Corrija isso antes que qualquer outra coisa acesse a máquina.
mc armazena aliases em ~/.mc/config.json como texto simples, portanto essas credenciais ficam no diretório pessoal de quem executou o comando. Executar mc com sudo coloca as credenciais de root em /root/.mc/config.json. Mantenha o alias de root em uma única conta de administrador e forneça a cada aplicação sua própria chave.
Entregar um objeto com uma URL pré-assinada
Uma URL pré-assinada é um link HTTPS comum com uma assinatura e um prazo de validade. Qualquer pessoa que tenha o link pode baixar esse objeto sem uma conta e sem um cliente.
mc share download --expire 12h local/backups/hello.txtA saída contém X-Amz-Signature e X-Amz-Expires na string de consulta. Há dois pontos que costumam surpreender. O link é criado a partir do endpoint do alias usado. Portanto, um alias em 127.0.0.1 gera um link que somente esta máquina pode abrir. Crie um segundo alias no seu hostname público para os links que pretende enviar. Além disso, não há um botão para revogar o link. A assinatura continua válida até expirar. Portanto, um prazo curto é o único controle disponível. Sete dias é o máximo permitido pelo formato de assinatura do S3.
Dê ao restic sua própria chave e bucket
As credenciais de root podem ler e excluir todos os buckets, portanto um job de backup não deve usá-las. Crie um bucket, uma policy limitada a esse bucket e um usuário que não tenha nenhum outro acesso.
mc mb local/restic
cat > /tmp/restic-rw.json <<'EOF'
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:ListBucket", "s3:GetBucketLocation"],
"Resource": ["arn:aws:s3:::restic"]
},
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
"Resource": ["arn:aws:s3:::restic/*"]
}
]
}
EOF
RESTIC_KEY=$(openssl rand -base64 24)
mc admin policy create local restic-rw /tmp/restic-rw.json
mc admin user add local restic-backup "$RESTIC_KEY"
mc admin policy attach local restic-rw --user restic-backupO MinIO fornece uma policy integrada readwrite que reduziria o número de comandos em um, mas concede acesso total a todos os buckets do servidor. A policy acima especifica o bucket duas vezes de propósito: uma vez como arn:aws:s3:::restic, para que a listagem do bucket funcione, e outra como arn:aws:s3:::restic/*, para os objetos dentro dele. No S3, um bucket e seus objetos são recursos separados. Portanto, uma policy que especifique apenas um deles falha de uma forma que parece indicar um problema no cliente.
Teste a limitação antes de confiar nela.
mc alias set resticuser http://127.0.0.1:9000 restic-backup "$RESTIC_KEY"
mc ls resticuser/restic
mc ls resticuser/backupsO primeiro ls funciona e o segundo falha com Access Denied. Uma policy que não foi testada é apenas uma suposição.
Agora aponte o restic para o bucket. O restic lê as credenciais do S3 pelas variáveis de ambiente padrão da AWS, portanto não há um arquivo de credenciais específico do restic.
sudo apt install -y restic
export AWS_ACCESS_KEY_ID=restic-backup
export AWS_SECRET_ACCESS_KEY="$RESTIC_KEY"
restic -r s3:http://127.0.0.1:9000/restic init
restic -r s3:http://127.0.0.1:9000/restic backup /etc
restic -r s3:http://127.0.0.1:9000/restic snapshotsrestic init solicita uma senha para o repositório. Essa senha criptografa o repositório. Assim, o MinIO armazena apenas texto cifrado, e perder a senha significa perder o backup. Uma execução iniciada por um timer do systemd não tem um terminal para receber a senha. Portanto, defina RESTIC_PASSWORD_FILE como um arquivo com modo 600 para os backups agendados.
Uma regra de posicionamento é mais importante do que qualquer comando acima. Um repositório do restic no mesmo VPS que os dados protegidos evita um rm e nada mais. O nó do MinIO deve estar em outra máquina, de preferência em outra região. backups do restic em um VPS explica o agendamento e a retenção com base nisso.
Encerrar TLS com nginx
O MinIO fica em localhost, portanto o nginx é a superfície pública. Emita primeiro o certificado, conforme descrito em Certificados Let's Encrypt com certbot e nginx, e depois use este bloco de servidor.
server {
listen 443 ssl;
server_name s3.example.com;
ignore_invalid_headers off;
client_max_body_size 0;
proxy_buffering off;
proxy_request_buffering off;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 300;
proxy_http_version 1.1;
proxy_set_header Connection "";
chunked_transfer_encoding off;
proxy_pass http://127.0.0.1:9000;
}
}Várias dessas linhas são essenciais. client_max_body_size 0 remove o limite padrão de 1 MB para o corpo da requisição. Sem isso, qualquer upload maior é rejeitado com 413 Request Entity Too Large antes mesmo de o MinIO receber a requisição. proxy_request_buffering off encaminha o upload diretamente, porque o comportamento padrão grava primeiro a requisição inteira em um arquivo temporário. Assim, um objeto grande precisa do dobro de espaço em disco. proxy_set_header Host $http_host é o ponto mais sutil: uma assinatura S3 cobre o cabeçalho Host. Portanto, um proxy que o reescreve faz todas as requisições falharem com SignatureDoesNotMatch, embora o access log mostre a chegada de uma requisição normal.
Informe também ao MinIO o nome público, para que os links gerados apontem para o proxy, e não para localhost.
echo 'MINIO_SERVER_URL=https://s3.example.com' | sudo tee -a /etc/default/minio
sudo systemctl restart minioO firewall permanece simples. Permita SSH e HTTPS. Não crie nenhuma regra para as portas 9000 e 9001, porque um endereço associado a 127.0.0.1 não pode ser acessado de outra máquina, independentemente da configuração do firewall. Noções básicas do firewall ufw em um VPS contém os comandos.
Quando o MinIO de nó único é suficiente e quando você precisa de um S3 de verdade
Nó único significa um disco sem paridade. A própria documentação do MinIO descreve esse layout como adequado para testes e cargas pequenas sem requisito de disponibilidade. Não há uma segunda cópia dentro da implantação, portanto a durabilidade de cada objeto é a durabilidade do disco de uma única VPS. Os recursos que pressupõem um backend distribuído com codificação de apagamento, incluindo replicação de buckets e bloqueio de objetos, pertencem a implantações com vários discos. Portanto, não prometa a ninguém uma política de retenção imutável nesse setup.
Ele é adequado como destino do restic em uma segunda VPS de outra região e como endpoint S3 para desenvolvimento e artefatos de CI, quando perder um bucket exige apenas uma nova compilação. Também é uma opção razoável para uploads de usuários em uma aplicação pequena, desde que você seja responsável pelo plano de recuperação e tenha testado uma restauração de fato.
Escolha S3 gerenciado quando um contrato ou órgão regulador exigir bloqueio de objetos ou durabilidade em várias regiões, ou quando você preferir não ser a pessoa chamada às 03:00 porque um disco ficou cheio. O build congelado é outro motivo legítimo. Em julho de 2026, o binário comunitário pré-compilado é de setembro de 2025 e não recebe correções. Executá-lo significa aceitar essa condição, ou compilar a partir do código-fonte e acompanhar o projeto por conta própria.
Vale declarar um limite porque essa dúvida surge com frequência. Armazenamento de objetos não é um banco de dados. Cada gravação substitui o objeto inteiro, portanto um arquivo SQL ativo em um bucket S3 é lento e inseguro. Mantenha o banco de dados no disco local e faça backup dele no bucket: executar SQLite em produção em uma VPS descreve essa separação.
Modos de falha e mensagens que você verá
A unidade falha logo depois de systemctl enable --now. Leia journalctl -u minio -n 30 --no-pager. Failed to load environment files: No such file or directory significa que /etc/default/minio está ausente ou que o caminho está escrito incorretamente na unidade. Uma mensagem terminada em permission denied significa que o diretório de dados não pode ser gravado pela conta de serviço. Portanto, verifique se stat -c '%U' /var/lib/minio/data exibe minio-user.
minioadmin:minioadmin ainda permite o login. O arquivo de ambiente nunca chegou ao processo. Confirme que a unidade contém EnvironmentFile=/etc/default/minio, execute sudo systemctl daemon-reload e reinicie o serviço. O MinIO lê as credenciais root uma vez na inicialização. Portanto, editar esse arquivo sem reiniciar não altera nada.
Address already in use na inicialização. Outro processo está usando a porta 9000. Encontre-o com sudo ss -ltnp | grep :9000 antes de alterar a porta do MinIO.
Os uploads com mais de 1 MB falham pelo proxy. O nginx respondeu 413 Request Entity Too Large, e o MinIO nunca recebeu a solicitação. Defina client_max_body_size 0 no bloco do servidor.
SignatureDoesNotMatch. A chave secreta está incorreta ou algo entre o cliente e o MinIO reescreveu o cabeçalho Host, que é incluído na assinatura.
RequestTimeTooSkewed. O relógio do cliente ou do servidor está incorreto. Toda solicitação S3 inclui um timestamp e é rejeitada fora de uma janela de 15 minutos. Verifique timedatectl e confirme que a sincronização de horário está ativa.
Access Denied em um bucket que você sabe que existe. A chave está limitada a outro bucket. Exiba o que a política realmente permite com mc admin policy info local restic-rw e compare o nome do bucket nas linhas de recurso.
FAQ
O MinIO de nó único é suficiente para backups reais?
Ele é suficiente como destino do restic executado em uma máquina separada dos dados que protege. Não é suficiente como sua única cópia. Uma implantação com uma única unidade não tem paridade, portanto não há uma segunda cópia dentro do MinIO. Se o disco da VPS perder dados, os objetos serão perdidos. Mantenha um segundo destino em outro local e faça uma restauração de ambos pelo menos uma vez para confirmar que o processo funciona.
Por que sha256sum -c falha no arquivo de checksums do MinIO?
Porque o rótulo após o hash nesse arquivo identifica a release, minio.RELEASE.2025-09-07T16-13-09Z, enquanto o arquivo baixado normalmente se chama minio. sha256sum -c procura um arquivo com o nome escrito no arquivo de checksums, não o encontra e informa No such file or directory e WARNING: 1 listed file could not be read. O download está correto. Compare diretamente as strings dos hashes e ignore o rótulo, que não tem significado de segurança.
O que aconteceu com o console web de administração do MinIO?
O MinIO removeu os recursos de administração do console da edição comunitária em maio de 2025, deixando um navegador de objetos na interface web. Os buckets e usuários agora são gerenciados com o cliente mc, usando comandos como mc admin user add e mc admin policy attach. Esse é o procedimento suportado na edição comunitária, não uma solução alternativa. Por isso, este guia faz tudo pela linha de comando.
Como aponto o restic para o MinIO como backend S3?
Defina AWS_ACCESS_KEY_ID e AWS_SECRET_ACCESS_KEY com uma access key do MinIO e o secret correspondente. Em seguida, use uma string de repositório no formato s3:https://s3.example.com/restic, em que o último elemento do caminho é o nome do bucket. Crie o bucket primeiro com mc mb, porque uma key limitada a um bucket não tem permissão para criar buckets. O restic criptografa tudo com a própria senha do repositório antes do upload. Assim, o MinIO armazena ciphertext e nunca acessa seus arquivos.
Preciso executar o MinIO atrás do nginx?
Você precisa de TLS (transport layer security) sempre que um cliente não estiver na mesma máquina, porque as credenciais S3 e os dados dos objetos trafegam dentro da requisição. Um proxy na porta 443 com um certificado do certbot é a forma mais simples de fazer isso. Ele também mantém a renovação do certificado fora do MinIO. O MinIO também pode terminar o TLS diretamente se você apontar --certs-dir para um diretório que contenha public.crt e private.key. Nesse caso, a conta de serviço precisa de acesso de leitura à chave privada renovada, o que exige trabalho adicional para obter o mesmo resultado.