Ubuntu 24.04에 MinIO 직접 설치 및 구축 방법
MinIO 커뮤니티 에디션 배포 중단 이후 소스 빌드를 통해 Ubuntu 24.04에 S3 호환 객체 스토리지를 구축하는 방법을 설명합니다. systemd 설정, mc 클라이언트 활용, restic 백업 구성 및 410 Gone 오류 대응을 포함한 실무 가이드를 제공합니다.
MinIO를 이용한 자체 호스팅 객체 스토리지의 이점
MinIO는 Amazon S3 API를 지원하는 자체 호스팅 객체 스토리지입니다. restic이나 S3 SDK를 자신의 서버로 향하게 하고 엔드포인트 설정 하나만 변경하면, 일상적인 객체 읽기, 쓰기, 목록 조회 및 사전 서명된 링크(presigned links) 기능을 즉시 사용할 수 있습니다. 다만, 호환성이 곧 동일함을 의미하지는 않습니다. 예를 들어 MinIO는 Amazon의 객체 ACL을 구현하지 않으며, 관리자 측에서도 이를 구현할 의사가 없음을 밝혔습니다. 따라서 ACL 동작에 의존하는 애플리케이션은 단순히 엔드포인트를 바꾸는 것 이상의 작업이 필요합니다. 이 가이드는 Ubuntu 24.04 환경에서 단일 노드를 구축하는 방법을 다룹니다. 검증된 소스에서 컴파일된 바이너리, 전용 시스템 사용자, 루트 자격 증명을 unit 파일에 노출하지 않는 systemd unit, 그리고 restic이 백업을 저장할 버킷을 구성합니다.
S3(Simple Storage Service)는 파일 시스템이 아닌 HTTP API입니다. 버킷 내의 특정 키(key)에 객체를 PUT으로 저장하고 GET으로 다시 가져오는 방식이며, 표준 버킷에서는 부분 쓰기나 이름 변경이 불가능합니다. Amazon의 S3 Express One Zone 디렉터리 버킷은 예외적으로 자체적인 추가(append) 및 이름 변경 호출을 지원하지만, 이는 특정 스토리지 클래스일 뿐이며 이 가이드의 내용은 이에 의존하지 않습니다. 백업 도구는 객체가 온전하게 도착했거나 아예 도착하지 않았거나 둘 중 하나인 표준 모델을 선호합니다.
단일 노드는 데이터의 복사본 하나를 보관합니다. 이것이 여러분이 감수해야 할 트레이드오프입니다. VPS 비용으로 직접 제어 가능한 S3 엔드포인트를 얻는 대신, 고장 난 디스크 교체부터 서버 소프트웨어 패치에 이르기까지 클라우드 제공업체가 수행하던 모든 관리 작업을 직접 떠안게 됩니다. 이 트레이드오프가 언제 유효한지에 대해서는 마지막 부분에서 명확히 설명합니다.
2026년 10월 기준 MinIO 커뮤니티 에디션 현황
이 내용을 기반으로 작업을 시작하기 전에 반드시 읽어보시기 바랍니다. 최근 변경 사항이 있기 때문입니다. 2025년 5월, MinIO는 커뮤니티 에디션의 웹 콘솔에서 관리 기능을 제거했습니다. 브라우저에 남은 기능은 객체 브라우저뿐이므로, 버킷과 액세스 키는 이제 mc 명령줄 클라이언트를 사용하여 관리해야 합니다.
2025년 하반기에 MinIO는 커뮤니티용 사전 컴파일 바이너리 배포를 중단했습니다. 프로젝트 README에 따르면 커뮤니티 에디션은 소스 코드로만 배포되며, 2026년 4월부터 minio/minio 저장소는 THIS REPOSITORY IS NO LONGER MAINTAINED 공지와 함께 보관(archived) 처리되었습니다. 기존 다운로드 서버 역시 커뮤니티 파일 제공을 중단했습니다. 2026년 10월 20일 확인 결과, 이 가이드에서 서버 바이너리, 클라이언트 바이너리 및 체크섬 파일을 다운로드하는 데 사용했던 모든 dl.min.io 경로는 410 Gone 응답을 반환합니다. 이는 일시적인 장애가 아니라 의도적인 제거입니다. 해당 서버에 남아 있는 유일한 mc 바이너리는 상용 제품군에 속하는 /aistor/mc/release/의 AIStor Client뿐입니다. 현재 min.io의 다운로드 페이지에는 AIStor Server, AIStor Client, AIStor SDK 및 몇 가지 AIStor 애드온이 나열되어 있으며, 개발자, 연구원 및 독립형 배포를 운영하는 소규모 조직을 대상으로 하는 무료 AIStor 티어가 있을 뿐 커뮤니티 빌드는 없습니다.
소스 코드는 여전히 존재하며 마지막 바이너리보다 최신 버전입니다. 마지막 커뮤니티 릴리스는 RELEASE.2025-10-15T17-29-55Z이며, 2025년 10월 16일에 보안 릴리스로 게시되었습니다. 이 릴리스는 서비스 계정 및 STS의 세션 정책 우회를 통한 권한 상승 문제를 해결했으며, 릴리스 노트는 모든 사용자에게 즉시 업그레이드할 것을 권고합니다. 마지막으로 사전 컴파일된 바이너리는 2025년 9월 빌드였으므로 해당 수정 사항이 반영되지 않았습니다. 이 가이드는 10월 릴리스를 직접 컴파일하며, 저장소가 보관 처리되었으므로 이것이 커뮤니티 에디션이 받게 될 마지막 수정 사항이기도 합니다.
이 사실 하나가 이 가이드의 나머지 내용을 결정합니다. 이 가이드에서 MinIO가 127.0.0.1에서 수신 대기하고 사용자가 제어하는 프록시를 통해서만 인터넷에 연결하도록 설정하는 이유가 바로 여기에 있습니다.
MinIO 바이너리 설치 및 다운로드 확인
MinIO는 Go로 작성되었으며, 공급업체의 README는 Go 1.24 이상이 필요한 go install 명령 하나로 빌드하도록 안내합니다. 먼저 공식 tarball에서 Go를 설치하십시오. 2026년 10월 20일 기준으로 go.dev/dl의 현재 안정화 릴리스는 go1.27.3이며, 2026년 10월 19일에 crypto/x509 패키지의 보안 수정 사항을 포함하여 배포되었습니다. 아래 해시는 해당 페이지에 게시된 linux-amd64 아카이브의 해시입니다. 더 최신 버전의 Go를 선택한다면 동일한 페이지에서 해당 해시를 확인하십시오. -f 플래그를 사용하면 HTTP 오류 발생 시 오류 페이지를 지정한 이름으로 저장하지 않고 curl이 실패하도록 만듭니다. 이 플래그를 사용하지 않으면 404 페이지를 아카이브로 착각하여 압축을 풀고 왜 작동하지 않는지 의문을 갖게 됩니다.
cd /tmp
GOVER=go1.27.3
curl -fsSL "https://go.dev/dl/$GOVER.linux-amd64.tar.gz" -o go.tar.gz두 해시를 비교하십시오. 해시만 비교해야 합니다.
published=65efbc80fdc1fa2cd2631cad5b481777a68e8725f34e89f50fc6ac714e379b40
downloaded=$(sha256sum go.tar.gz | awk '{print $1}')
[ "$published" = "$downloaded" ] && echo "checksum ok"이 검사가 무엇을 증명하는지 명확히 이해해야 합니다. 일치한다는 것은 다운로드가 완료되었으며 전송 과정에서 손상되거나 변경되지 않았음을 증명합니다. 공급업체가 신뢰할 수 있다는 것을 증명하는 것은 아닙니다. 그것은 별개의 문제이며 어떤 sha256sum 명령으로도 해결할 수 없습니다.
sudo rm -rf /usr/local/go
sudo tar -C /usr/local -xzf go.tar.gz
export PATH=$PATH:/usr/local/go/bin
go version이제 일반 사용자 권한으로 최종 커뮤니티 릴리스를 빌드하십시오. go install는 다운로드하는 모든 모듈(MinIO 소스 코드 포함)을 sum.golang.org에 있는 Go의 공개 체크섬 데이터베이스와 대조합니다. 따라서 변조되거나 교체된 모듈이 있다면 빌드가 보안 오류와 함께 중단되며 바이너리가 생성되지 않습니다.
go install github.com/minio/minio@RELEASE.2025-10-15T17-29-55Z
sudo install -o root -g root -m 755 ~/go/bin/minio /usr/local/bin/minio
minio --version
go version -m /usr/local/bin/minio | head -n 3minio --version은 릴리스 이름이 있어야 할 자리에 DEVELOPMENT.GOGET를 출력합니다. 이는 go install이 공급업체가 자체 바이너리에 표시를 남길 때 사용한 빌드 플래그를 적용하지 않기 때문입니다. go version -m는 Go가 모든 바이너리에 포함하는 모듈 정보를 읽으며, mod github.com/minio/minio 줄은 빌드한 커밋을 나타냅니다. 여기서 Permission denied이 나타난다면 모드가 잘못된 것이며, command not found이 나타난다면 /usr/local/bin이 PATH에 없는 것입니다. 만약 VPS의 메모리가 부족하여 컴파일할 수 없다면, 동일한 아키텍처의 다른 Linux 머신에서 동일한 go install을 실행한 뒤 바이너리를 복사해 오십시오.
시스템 사용자 및 데이터 디렉터리 생성
MinIO는 네트워크를 통해 업로드를 수신하므로 root 권한으로 실행해서는 안 됩니다. 홈 디렉터리와 로그인 셸이 없는 계정을 할당하십시오.
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은 1000 미만의 UID를 가진 시스템 계정을 생성하며, 이는 일반 사용자 계정 범위와 겹치지 않게 합니다. -M는 홈 디렉터리를 생성하지 않는데, 로그인하지 않는 계정은 홈 디렉터리가 필요 없기 때문입니다. id minio-user으로 결과를 확인하고, stat -c '%U %a' /var/lib/minio을 실행하여 minio-user 750가 출력되는지 확인하십시오.
데이터 디렉터리는 읽기 전용이 아니라 해당 사용자가 쓰기 권한을 가져야 합니다. MinIO는 최초 시작 시 자체 설정을 저장하기 위해 볼륨 내부에 .minio.sys 디렉터리를 생성합니다. 따라서 root 소유의 디렉터리를 사용하면 MinIO는 시작 과정에서 permission denied로 끝나는 메시지를 출력하며 종료됩니다. 동일한 규칙이 이러한 방식으로 실행하는 모든 서비스에 적용되며, VPS에서의 최소 권한 서비스 사용자 문서에서 이를 상세히 다룹니다.
루트 자격 증명을 환경 파일에 저장하기
루트 자격 증명은 모든 버킷에 대한 접근 권한을 가지므로, 누구나 읽을 수 있는 unit 파일에 포함해서는 안 됩니다. 먼저 적절한 권한 모드로 파일을 생성한 뒤 내용을 작성하십시오. 이렇게 하면 비밀번호가 읽기 가능한 상태로 파일에 노출되는 시간을 방지할 수 있습니다.
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는 파일을 새로 생성하는 대신 기존 파일을 잘라내므로, 파일 모드는 600으로 유지되고 소유자도 root로 유지됩니다. 이는 의도된 동작입니다. systemd는 User=로 권한을 낮추기 전에 root 권한으로 EnvironmentFile을 읽습니다. 따라서 서비스 계정이 자신의 자격 증명을 직접 읽을 필요가 없습니다. 서비스가 실행되면 sudo -u minio-user cat /etc/default/minio을 사용하여 확인하십시오. 해당 명령은 반드시 Permission denied를 출력해야 합니다.
MinIO를 시작하기 전에 알아두어야 할 두 가지 동작이 있습니다. 환경 변수에 MINIO_ROOT_USER과 MINIO_ROOT_PASSWORD이 없더라도 MinIO는 시작을 거부하지 않습니다. MinIO는 문서화된 기본 자격 증명인 minioadmin:minioadmin로 시작하는데, 이는 스캐너가 가장 먼저 시도하는 조합이며 이 상태에서도 서비스는 정상적으로 작동하는 것처럼 보입니다. 반면 8자 미만의 비밀번호는 거부됩니다. MinIO는 시작 시 자격 증명이 유효하지 않다는 오류를 내며 종료되는데, 이는 액세스 키는 최소 3자, 시크릿 키는 최소 8자 이상이어야 하기 때문입니다.
MINIO_VOLUMES은 데이터 경로이며 MINIO_OPTS에는 플래그가 포함됩니다. 127.0.0.1에 바인딩한다는 것은 아직 이 VPS 외부에서 S3 API에 접근할 수 없음을 의미하며, 이는 올바른 기본 설정입니다. 이후 인증서를 보유한 프록시를 통해 의도적으로 접근을 허용하게 됩니다.
systemd 유닛 작성
/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.targetEnvironmentFile에 -이 없는 것은 오타가 아니라 의도적인 결정입니다. 대시(-)를 사용하면 systemd는 파일이 없어도 이를 무시하고 MinIO를 시작하므로, 파일이 삭제되었거나 경로가 잘못되어도 서버가 minioadmin:minioadmin에서 기본값으로 조용히 실행됩니다. 대시가 없으면 파일이 누락될 경우 MinIO가 실행되기 전에 유닛이 실패하며, journalctl -u minio은 Failed to load environment files: No such file or directory을 표시합니다. 서버가 기본 비밀번호를 조용히 수락하는 것보다, 시작을 거부하는 유닛을 확인하는 것이 훨씬 쉽습니다.
$MINIO_VOLUMES와 $MINIO_OPTS에 따옴표를 사용하지 않은 이유는 systemd가 따옴표가 없는 변수를 공백 기준으로 나누어 별도의 인자로 처리하기 때문입니다. 이것이 MINIO_OPTS의 네 단어가 minio server에 네 개의 인자로 전달되는 방식입니다. LimitNOFILE=65536은 파일 디스크립터 제한을 높입니다. 모든 열린 연결과 데이터 파일은 디스크립터를 하나씩 소모하며, 기본값인 1024는 부하가 걸리면 금방 고갈되기 때문입니다.
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은 active을 출력해야 하며, 상태 확인 엔드포인트는 200로 응답해야 합니다. journalctl -u minio -n 20 --no-pager은 서버가 수신 대기 중인 API 주소를 보여줍니다. 유닛이 계속 재시작된다면 systemd는 포기하고 Start request repeated too quickly을 기록합니다. 이는 MinIO가 매 시도마다 종료된다는 의미입니다. 원인은 해당 메시지 바로 윗줄에 출력되므로 위쪽을 확인하십시오.
더 높은 격리를 원한다면 [Service] 섹션에 ProtectSystem=full와 ProtectHome=true을 추가하십시오. 두 설정 모두 호스트 커널의 마운트 네임스페이스가 필요합니다. OpenVZ나 LXC처럼 호스트 커널을 공유하는 컨테이너 가상화 환경에서는 이 설정이 실패할 수 있으며, 유닛은 status=226/NAMESPACE를 보고합니다. 이 두 줄을 제거하면 정상적으로 시작됩니다. 유닛 자체는 일반적인 형태이며, VPS에서의 systemd 서비스 및 타이머에서 나머지 지시어들을 다룹니다.
mc 설치 및 왕복 테스트 수행
MinIO 클라이언트는 mc입니다. apt install mc를 사용하여 설치하지 마십시오. 해당 패키지는 MinIO와 무관한 파일 관리자인 Midnight Commander입니다.
dl.min.io는 여전히 mc 바이너리를 제공하지만, 커뮤니티 버전은 아닙니다. 이전 커뮤니티 경로인 https://dl.min.io/client/mc/release/linux-amd64/mc은 410 Gone을 반환합니다. 현재 MinIO가 해당 위치에서 제공하는 것은 https://dl.min.io/aistor/mc/release/linux-amd64/mc의 AIStor Client이며, AIStor CLI 참조 문서에서는 이를 라이선스가 있는 AIStor Server 배포판 전용 기능을 갖춘 도구로 설명합니다. 해당 참조 문서에는 커뮤니티 서버에 사용하는 방법에 대한 언급이 없으므로, 이 가이드에서는 커뮤니티 클라이언트를 사용합니다. minio/mc 저장소는 2026년 7월 이후 보관(archive) 처리되었으므로, 동일한 Go 툴체인을 사용하여 마지막 릴리스인 RELEASE.2025-08-13T08-35-41Z을 빌드하십시오. 체크섬 데이터베이스에서도 이 다운로드를 지원합니다.
go install github.com/minio/mc@RELEASE.2025-08-13T08-35-41Z
sudo install -o root -g root -m 755 ~/go/bin/mc /usr/local/bin/mc서버를 별칭(alias)으로 등록한 다음, 객체를 이동시켜 보십시오.
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는 hello.txt과 그 크기를 나열해야 하며, mc cat는 hello object storage를 출력해야 합니다. 이 왕복 테스트는 서버가 정상 작동한다는 실질적인 증거입니다. 다른 모든 클라이언트가 수행하는 것과 동일한 서명된 S3 요청을 생성하기 때문입니다. 추가 확인이 필요하다면 mc admin info local을 실행하여 서버 상태를 출력하십시오.
서버가 비어 있는 지금, 마지막으로 한 가지 점검을 수행하십시오.
mc alias set defaultcheck http://127.0.0.1:9000 minioadmin minioadmin이 명령은 반드시 실패해야 합니다. 만약 성공한다면 환경 파일이 프로세스에 전달되지 않은 것이며, 서버가 기본 자격 증명으로 실행 중인 상태입니다. 다른 작업이 기기에 접근하기 전에 이 문제를 먼저 해결하십시오.
mc은 별칭을 ~/.mc/config.json에 일반 텍스트로 저장하므로, 해당 자격 증명은 명령을 실행한 사용자의 홈 디렉터리에 남게 됩니다. sudo 권한으로 mc를 실행하면 루트 자격 증명이 /root/.mc/config.json에 저장됩니다. 루트 별칭은 관리자 계정 하나에만 유지하고, 각 애플리케이션에는 고유한 키를 부여하십시오.
사전 서명된 URL로 객체 하나 공유하기
사전 서명된 URL은 서명과 만료 시간이 포함된 일반적인 HTTPS 링크입니다. 이 링크를 가진 사람은 누구나 계정이나 클라이언트 없이 해당 객체를 가져올 수 있습니다.
mc share download --expire 12h local/backups/hello.txt출력 결과에는 쿼리 문자열에 X-Amz-Signature와 X-Amz-Expires이 포함됩니다. 이와 관련하여 두 가지 주의할 점이 있습니다. 링크는 사용한 별칭(alias)의 엔드포인트로부터 생성되므로, 127.0.0.1에 설정된 별칭으로 만든 링크는 해당 장비에서만 열 수 있습니다. 외부로 보낼 링크라면 공용 호스트 이름으로 별칭을 하나 더 만드십시오. 또한 링크별로 즉시 취소할 수 있는 기능은 없습니다. 서명은 만료될 때까지, 혹은 서명에 사용된 키가 삭제되거나 변경될 때까지 유효합니다. 루트 자격 증명으로 서명된 링크의 경우 키를 교체해야만 무효화할 수 있으므로, 짧은 만료 시간을 설정하는 것이 실질적인 통제 수단입니다. S3 서명 형식에서 허용하는 최대 만료 기간은 7일입니다.
restic에 전용 키와 버킷 할당하기
root 자격 증명은 모든 버킷을 읽고 삭제할 수 있으므로, 백업 작업에 이를 사용해서는 안 됩니다. 전용 버킷을 생성하고, 해당 버킷으로 범위를 제한한 정책을 설정한 뒤, 다른 권한이 없는 사용자를 생성하십시오.
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-backupMinIO는 내장 readwrite 정책을 제공하며, 이를 사용하면 명령어를 한 줄 줄일 수 있지만, 서버의 모든 버킷에 대한 전체 접근 권한을 부여하게 됩니다. 위 정책에서 버킷 이름을 두 번 명시한 것은 의도적입니다. 한 번은 버킷 목록 조회를 위해 arn:aws:s3:::restic로, 다른 한 번은 내부 객체 접근을 위해 arn:aws:s3:::restic/*으로 지정했습니다. S3에서 버킷과 그 안의 객체는 별개의 리소스이므로, 둘 중 하나만 명시한 정책은 클라이언트가 고장 난 것처럼 보이는 오류를 발생시킵니다.
신뢰하기 전에 제한 사항을 먼저 테스트하십시오.
mc alias set resticuser http://127.0.0.1:9000 restic-backup "$RESTIC_KEY"
mc ls resticuser/restic
mc ls resticuser/backups첫 번째 ls는 성공하지만, 두 번째는 Access Denied 오류와 함께 실패합니다. 테스트하지 않은 정책은 추측에 불과합니다.
이제 restic이 해당 버킷을 바라보도록 설정하십시오. restic은 표준 AWS 환경 변수에서 S3 자격 증명을 읽어오므로, 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은 저장소 암호를 요구합니다. 이 암호는 저장소를 암호화하므로 MinIO는 암호문만 저장하게 되며, 암호를 분실하면 백업 데이터도 복구할 수 없습니다. systemd 타이머로 실행되는 작업은 암호를 입력할 터미널이 없으므로, 예약 백업을 위해 RESTIC_PASSWORD_FILE을 모드 600 파일로 설정하십시오.
배치 규칙 하나가 위에서 언급한 모든 명령어보다 중요합니다. 데이터를 보호하는 것과 동일한 VPS에 restic 저장소를 두는 것은 실수로 인한 rm 상황에서만 보호해 줄 뿐, 그 외의 상황에서는 아무런 도움이 되지 않습니다. MinIO 노드는 다른 머신에 위치해야 하며, 가급적 다른 리전에 두는 것이 좋습니다. VPS에서의 restic 백업에서 백업 예약 및 보관 정책에 대해 다룹니다.
Nginx에서 TLS 종료하기
MinIO는 localhost에서 실행되므로 Nginx가 외부 공개 인터페이스 역할을 합니다. certbot과 Nginx를 이용한 Let's Encrypt 인증서 발급에서 설명한 대로 먼저 인증서를 발급받은 뒤, 아래의 서버 블록을 사용하십시오.
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;
}
}위 설정 중 일부 행은 매우 중요합니다. client_max_body_size 0는 기본값인 1 MB 본문 크기 제한을 해제합니다. 이 설정을 하지 않으면 MinIO가 요청을 받기도 전에 413 Request Entity Too Large 오류로 인해 대용량 업로드가 거부됩니다. proxy_request_buffering off은 업로드를 즉시 스트리밍합니다. 기본 설정은 전체 요청을 임시 파일에 먼저 저장하므로, 대용량 객체 업로드 시 디스크 공간이 두 배로 필요하기 때문입니다. proxy_set_header Host $http_host는 주의가 필요한 설정입니다. S3 서명은 Host 헤더를 포함하므로, 프록시가 이 헤더를 재작성하면 접근 로그에는 정상적인 요청으로 기록되더라도 모든 요청이 SignatureDoesNotMatch 오류로 실패하게 됩니다.
MinIO가 생성하는 링크가 localhost가 아닌 프록시를 가리키도록, MinIO에게 자신의 공개 도메인 이름을 알려주어야 합니다.
echo 'MINIO_SERVER_URL=https://s3.example.com' | sudo tee -a /etc/default/minio
sudo systemctl restart minio방화벽 설정은 최소화합니다. SSH와 HTTPS만 허용하고, 9000번과 9001번 포트는 규칙을 추가하지 마십시오. 127.0.0.1에 바인딩된 주소는 방화벽 설정과 관계없이 외부 기기에서 직접 접근할 수 없기 때문입니다. VPS에서의 ufw 방화벽 기초에 관련 명령어가 정리되어 있습니다.
단일 노드 MinIO로 충분한 경우와 실제 S3가 필요한 경우
여기서 단일 노드란 드라이브 하나를 의미하며, 따라서 배포 내에 패리티나 두 번째 복사본이 존재하지 않고 모든 객체의 내구성은 VPS 디스크 하나의 내구성과 같습니다. 2025년 10월 호스팅된 커뮤니티 문서가 폐쇄된 이후 minio/docs 저장소의 소스로만 유지되고 있는 MinIO 자체 문서는, 독립형 배포를 초기 개발 및 평가용으로 명시하며 MinIO는 이를 운영 환경에 권장하지 않는다고 밝히고 있습니다. 버킷에 객체 잠금(Object locking)을 설정할 수는 있지만, 단일 VPS에서는 S3 API를 통한 삭제만 방지할 뿐입니다. 해당 장비의 root 권한을 가진 사람은 누구나 디스크의 파일을 삭제할 수 있으므로, 이 설정으로 누구에게도 변경 불가능한 보존 정책을 보장해서는 안 됩니다.
이 구성은 다른 지역의 두 번째 VPS에서 restic 대상 저장소로 사용하거나, 버킷을 잃어버려도 재빌드만 하면 되는 개발 작업 및 CI 아티팩트용 S3 엔드포인트로 적합합니다. 또한 복구 계획을 직접 수립하고 실제로 복원 테스트를 완료했다면, 소규모 애플리케이션의 사용자 업로드 용도로도 합리적입니다.
계약이나 규제 기관이 객체 잠금 또는 다중 지역 내구성을 요구하거나, 디스크 용량 부족으로 새벽 3시에 호출받는 사람이 되고 싶지 않다면 관리형 S3를 선택하십시오. 코드가 고정된 경우도 솔직한 이유가 될 수 있습니다. 2026년 10월 20일 기준으로 커뮤니티 에디션은 아카이브되어 더 이상 유지보수되지 않으며, 이 가이드가 다루는 마지막 릴리스는 RELEASE.2025-10-15T17-29-55Z입니다. 그 이후의 문제는 수정되지 않으므로, 이를 계속 사용하는 것은 해당 위험을 감수하거나 MinIO의 상용 AIStor 라인 또는 다른 자체 호스팅 저장소로 이전하는 것을 의미합니다. 자체 호스팅 MinIO 대안에서 이를 비교합니다.
자주 발생하는 문제이므로 한 가지 경계는 명확히 할 필요가 있습니다. 객체 스토리지는 데이터베이스가 아닙니다. 표준 버킷에서 모든 쓰기 작업은 객체 전체를 교체하며, Amazon이 S3 Express One Zone을 위해 추가한 append 호출조차 데이터 끝에 내용을 추가할 뿐이므로, S3 버킷에 있는 라이브 SQL 파일은 느리고 안전하지 않습니다. 데이터베이스는 로컬 디스크에서 운영하고 백업본을 버킷에 저장하십시오. VPS에서 SQLite 운영하기에서 이러한 분리 방식을 설명합니다.
실패 유형과 표시되는 메시지
유닛이 systemctl enable --now 직후에 실패합니다. journalctl -u minio -n 30 --no-pager를 읽어 보십시오. Failed to load environment files: No such file or directory은 /etc/default/minio이 없거나 유닛 파일 내 경로가 잘못되었음을 의미합니다. permission denied로 끝나는 메시지는 서비스 계정이 데이터 디렉터리에 쓰기 권한을 가지고 있지 않다는 뜻이므로, stat -c '%U' /var/lib/minio/data를 실행했을 때 minio-user이 출력되는지 확인하십시오.
minioadmin:minioadmin이 여전히 로그인됩니다. 환경 변수 파일이 프로세스에 전달되지 않았습니다. 유닛 파일에 EnvironmentFile=/etc/default/minio가 포함되어 있는지 확인하고, sudo systemctl daemon-reload을 실행한 뒤 서비스를 재시작하십시오. MinIO는 시작 시점에 루트 자격 증명을 한 번만 읽으므로, 재시작 없이 파일만 수정해서는 아무런 변화가 없습니다.
시작 시 Address already in use가 발생합니다. 다른 프로세스가 9000번 포트를 점유하고 있습니다. MinIO의 포트를 변경하기 전에 sudo ss -ltnp | grep :9000를 사용하여 해당 프로세스를 찾으십시오.
프록시를 통할 때 1 MB 이상의 업로드에 실패합니다. nginx가 413 Request Entity Too Large을 반환했으며 MinIO는 요청을 전달받지 못했습니다. 서버 블록에 client_max_body_size 0을 설정하십시오.
SignatureDoesNotMatch이 발생합니다. 시크릿 키가 잘못되었거나, 클라이언트와 MinIO 사이의 무언가가 서명에 포함된 Host 헤더를 재작성했을 가능성이 있습니다.
RequestTimeTooSkewed이 발생합니다. 클라이언트나 서버의 시간이 맞지 않습니다. 모든 서명된 S3 요청에는 타임스탬프가 포함되어 있으며, MinIO는 Amazon S3와 마찬가지로 헤더 서명된 요청의 타임스탬프가 서버 시간과 15분 이상 차이 나면 거부합니다. 미리 서명된 링크(Presigned links)는 만료 시간을 기준으로 검사합니다. timedatectl을 확인하고 시간 동기화가 활성화되어 있는지 확인하십시오.
존재하는 버킷임에도 Access Denied가 발생합니다. 키가 다른 버킷으로 범위가 제한되어 있습니다. mc admin policy info local restic-rw을 사용하여 정책이 실제로 허용하는 범위를 출력하고, 리소스 라인에 명시된 버킷 이름과 비교하십시오.
FAQ
단일 노드 MinIO가 실제 백업 용도로 충분합니까?
보호하려는 데이터가 있는 머신과 별도의 머신에서 실행되는 restic 대상으로는 충분합니다. 하지만 유일한 복사본으로 사용하기에는 부족합니다. 단일 드라이브 배포는 패리티가 없으므로 MinIO 내부에 두 번째 복사본이 존재하지 않으며, 해당 VPS 디스크에서 데이터가 손실되면 객체도 사라집니다. 다른 곳에 두 번째 대상을 유지하고, 복구 절차가 정상적으로 작동하는지 확인하기 위해 최소 한 번 이상 복구를 수행하십시오.
왜 sha256sum -c이 MinIO의 체크섬 파일에서 실패합니까?
dl.min.io가 2026년 10월 20일에 모든 커뮤니티 바이너리와 체크섬 파일을 확인하여 410 Gone에 답변했기 때문에, 해당 파일은 이전에 저장된 복사본에서만 마주하게 됩니다. 파일 내부의 해시 뒤에 있는 레이블은 minio.RELEASE.2025-09-07T16-13-09Z과 같은 릴리스를 가리키지만, 다운로드된 파일은 보통 minio로 불립니다. sha256sum -c은 체크섬 파일 내부에 적힌 이름의 파일을 찾지만 이를 찾지 못해 No such file or directory 및 WARNING: 1 listed file could not be read을 보고합니다. 다운로드 자체는 정상입니다. 보안상 의미가 없는 레이블은 무시하고 해시 문자열을 직접 비교하십시오.
MinIO 관리 웹 콘솔은 어디로 갔습니까?
MinIO는 2025년 5월에 커뮤니티 에디션 콘솔에서 관리 기능을 제거하고 웹 인터페이스에 객체 브라우저만 남겼습니다. 이제 버킷과 사용자는 mc admin user add 및 mc admin policy attach과 같은 명령어를 사용하는 mc 클라이언트로 관리합니다. 이것이 커뮤니티 에디션에서 의도한 방식이며 우회 방법이 아니므로, 이 가이드에서는 모든 작업을 명령줄에서 수행합니다.
restic에서 MinIO를 S3 백엔드로 지정하려면 어떻게 합니까?
AWS_ACCESS_KEY_ID와 AWS_SECRET_ACCESS_KEY를 MinIO 액세스 키와 시크릿으로 설정한 다음, 마지막 경로 요소가 버킷 이름인 s3:https://s3.example.com/restic 형식의 리포지토리 문자열을 사용하십시오. 특정 버킷으로 범위가 제한된 키는 버킷을 생성할 권한이 없으므로, mc mb을 사용하여 먼저 버킷을 생성하십시오. restic은 업로드 전에 자체 리포지토리 암호로 모든 데이터를 암호화하므로, MinIO는 암호문만 저장하며 사용자의 파일을 볼 수 없습니다.
MinIO를 반드시 nginx 뒤에서 실행해야 합니까?
클라이언트가 같은 머신에 있지 않은 경우 항상 TLS(전송 계층 보안)가 필요합니다. TLS가 없으면 모든 객체와 액세스 키 ID, 요청 서명이 평문으로 네트워크를 통과하기 때문입니다. S3 서명은 로컬에서 서명을 계산할 때만 시크릿 키를 사용하므로 시크릿 키 자체가 클라이언트를 떠나지는 않지만, 경로상의 누군가가 데이터를 읽거나 타임스탬프가 유효한 동안 캡처된 요청을 재전송할 수 있습니다. certbot에서 발급받은 인증서를 사용하여 443 포트에서 프록시를 운영하는 것이 TLS를 적용하는 가장 간단한 방법이며, 이렇게 하면 MinIO와 인증서 갱신을 분리하여 관리할 수 있습니다. MinIO가 직접 TLS를 종료하게 하려면 --certs-dir이 public.crt 및 private.key을 포함하는 디렉터리를 가리키도록 설정할 수 있지만, 이 경우 서비스 계정에 갱신된 개인 키에 대한 읽기 권한이 필요하므로 동일한 결과를 얻기 위해 추가 작업이 발생합니다.