Install MinIO on AlmaLinux 10 with Nginx and ZeroSSL
MinIO gives you a high-performance, S3-compatible object storage server that fits well on a small VPS when you want private buckets, simple API-compatible storage, and a clean web console without deploying a larger storage platform.
In this guide, we restore a fresh AlmaLinux 10.1 server on Shape.Host, verify the latest MinIO community release from the official project, install the current stable Go toolchain from the official Go download site, build MinIO from the exact upstream release tag using MinIO’s own release-aware ldflags path, configure the official MinIO systemd service template, publish the MinIO Console through Nginx on tutorials.shape.host, secure it with a trusted ZeroSSL certificate, and validate the finished deployment from both the terminal and a browser.
| Application | MinIO |
|---|---|
| Application version | RELEASE.2025-10-15T17-29-55Z |
| Operating system | AlmaLinux 10.1 |
| Build toolchain | Go 1.26.1 |
| Reverse proxy | Nginx 1.26.3 |
| Public hostname | tutorials.shape.host |
| TLS issuer | ZeroSSL ECC Domain Secure Site CA |
| Validated on | Live Shape.Host AlmaLinux 10.1 server |
Why Use MinIO on AlmaLinux 10?
- AlmaLinux 10.1 gives you a current RHEL-compatible base for a storage service you want to keep stable.
- The current MinIO community distribution is source-only, so you can build the latest release directly from upstream on your own server.
- The official MinIO service repository provides a maintained systemd template that is a better baseline than inventing a custom unit from scratch.
- Nginx plus ZeroSSL gives you a clean public HTTPS endpoint for the MinIO Console while the native MinIO ports stay local to the server.
Before You Begin
Make sure the following prerequisites are in place before you start:
- A fresh AlmaLinux 10 server
- Root or sudo access
- A DNS record pointing
tutorials.shape.hostto your server IP - Ports
80and443open to the internet - Your ZeroSSL EAB key ID and EAB HMAC key for ACME account registration
1. Verify the AlmaLinux 10 Release
Start by confirming that the restored server is actually running AlmaLinux 10.1.
cat /etc/os-release

2. Install Go, Nginx, Firewalld, and Base Dependencies
The MinIO upstream README now recommends installing the community edition from source, so this guide uses the current stable Go release from the official Go download site instead of relying on an older distro compiler. We also install Git because MinIO’s release-aware build path depends on the tagged upstream repository metadata, plus Nginx, firewalld, and the helper packages needed for certificate automation and SELinux-aware administration.
dnf -y install curl git tar gzip nginx openssl socat firewalld policycoreutils-python-utils
curl -fsSLO https://go.dev/dl/go1.26.1.linux-amd64.tar.gz
rm -rf /usr/local/go
tar -C /usr/local -xzf go1.26.1.linux-amd64.tar.gz
ln -sf /usr/local/go/bin/go /usr/local/bin/go
ln -sf /usr/local/go/bin/gofmt /usr/local/bin/gofmt
rm -f go1.26.1.linux-amd64.tar.gz
systemctl enable --now nginx firewalld
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
go version
nginx -v
firewall-cmd --state
getenforce
On the validated AlmaLinux 10.1 deployment, this installed the official Go 1.26.1 toolchain, Git 2.47.3, Nginx 1.26.3, and firewalld successfully, with SELinux reported as Disabled on this template.

3. Build MinIO from Source and Prepare the Official Service Configuration
The current MinIO community README explicitly says the community edition is source-only. In practice, building from a tagged checkout with MinIO’s own ldflags path gives you a release-aware binary, which is more useful for verification than a plain go install build that reports a development marker. For the service layer, the official minio-service repository provides the current systemd template and a two-file configuration layout under /etc/default/minio and /etc/minio/config.env.
export PATH=/usr/local/go/bin:/usr/local/bin:$PATH
id minio-user >/dev/null 2>&1 || useradd --system --home /var/lib/minio --shell /sbin/nologin minio-user
mkdir -p /mnt/data /etc/minio /var/lib/minio /usr/local/src
rm -rf /usr/local/src/minio
git clone --branch RELEASE.2025-10-15T17-29-55Z --depth 1 https://github.com/minio/minio.git /usr/local/src/minio
cd /usr/local/src/minio
VERSION="$(git describe --tags --abbrev=0 | sed 's#RELEASE\.\([0-9]\+\)-\([0-9]\+\)-\([0-9]\+\)T\([0-9]\+\)-\([0-9]\+\)-\([0-9]\+\)Z#\1-\2-\3T\4:\5:\6Z#')"
LDFLAGS="$(MINIO_RELEASE=RELEASE go run buildscripts/gen-ldflags.go "$VERSION")"
CGO_ENABLED=0 GOOS="$(go env GOOS)" GOARCH="$(go env GOARCH)" go build -tags kqueue -trimpath --ldflags "$LDFLAGS" -o ./minio 1>/dev/null
install -m 0755 ./minio /usr/local/bin/minio
curl -fsSL https://raw.githubusercontent.com/minio/minio-service/master/linux-systemd/minio.service -o /etc/systemd/system/minio.service
MINIO_ROOT_USER="minioadmin"
MINIO_ROOT_PASSWORD="$(openssl rand -base64 24 | tr -d '\n' | tr '/+' 'AB' | cut -c1-24)"
cat > /etc/default/minio <<'EOF'
MINIO_VOLUMES="/mnt/data"
MINIO_OPTS="--address 127.0.0.1:9000 --console-address 127.0.0.1:9001"
MINIO_CONFIG_ENV_FILE=/etc/minio/config.env
EOF
cat > /etc/minio/config.env <<EOF
MINIO_ROOT_USER=${MINIO_ROOT_USER}
MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD}
EOF
chown -R minio-user:minio-user /mnt/data /etc/minio /var/lib/minio
chmod 600 /etc/default/minio /etc/minio/config.env
systemctl daemon-reload
go version
minio --version | head -n 1
git -C /usr/local/src/minio describe --tags --abbrev=0
grep '^MINIO_VOLUMES=' /etc/default/minio
grep '^MINIO_OPTS=' /etc/default/minio
grep '^MINIO_CONFIG_ENV_FILE=' /etc/default/minio
grep '^MINIO_ROOT_USER=' /etc/minio/config.env
grep '^MINIO_ROOT_PASSWORD=' /etc/minio/config.env | sed 's/=.*$/=[redacted]/'
grep '^User=' /etc/systemd/system/minio.service
grep '^Group=' /etc/systemd/system/minio.service
grep '^ExecStart=' /etc/systemd/system/minio.service
On the validated server, MinIO built cleanly from the pinned release tag, the binary was installed into /usr/local/bin/minio, the official service template referenced the local MinIO binary, and the generated root password stayed redacted in the verification output.

4. Start MinIO and Validate the Local Service
With the binary and service files in place, start MinIO under systemd and confirm that both the S3 API and the Console answer on localhost before you expose the Console through Nginx.
4.1 Start the MinIO Service
systemctl enable --now minio
sleep 20
systemctl is-active minio
On the live server, systemd enabled the MinIO service at boot, the first startup completed cleanly, and the final state check returned active.

4.2 Validate the MinIO Ports and Local Health Endpoints
systemctl status --no-pager minio | sed -n '1,15p'
ss -lntp | grep -E ':9000|:9001'
minio --version | head -n 1
curl -I http://127.0.0.1:9000/minio/health/live
curl -I http://127.0.0.1:9001/login
On the validated deployment, MinIO listened on 127.0.0.1:9000 for the API and 127.0.0.1:9001 for the Console, the S3 health endpoint returned a successful response, and the local Console login page answered correctly.

5. Configure Nginx, Firewalld, SELinux, and ZeroSSL for the MinIO Console
This guide publishes the MinIO Console on tutorials.shape.host through Nginx while keeping the native MinIO API and Console ports on localhost only. That gives you a trusted public HTTPS login page without exposing the raw service ports directly.
mkdir -p /var/www/_letsencrypt /etc/nginx/ssl/tutorials.shape.host
cat > /etc/nginx/conf.d/tutorials.shape.host.conf <<'EOF'
server {
listen 80;
server_name tutorials.shape.host;
location /.well-known/acme-challenge/ {
root /var/www/_letsencrypt;
default_type "text/plain";
}
client_max_body_size 0;
proxy_buffering off;
proxy_request_buffering off;
location / {
proxy_pass http://127.0.0.1:9001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $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;
}
}
EOF
firewall-cmd --permanent --add-service=http
firewall-cmd --permanent --add-service=https
firewall-cmd --reload
# Run this if SELinux is enforcing on your server.
setsebool -P httpd_can_network_connect 1
nginx -t
systemctl reload nginx
curl -fsSL https://get.acme.sh | sh -s email=contact@shape.host
/root/.acme.sh/acme.sh --set-default-ca --server zerossl
/root/.acme.sh/acme.sh --register-account --server zerossl --eab-kid YOUR_ZEROSSL_EAB_KID --eab-hmac-key YOUR_ZEROSSL_EAB_HMAC_KEY
/root/.acme.sh/acme.sh --issue --server zerossl --webroot /var/www/_letsencrypt -d tutorials.shape.host --keylength ec-256
/root/.acme.sh/acme.sh --install-cert -d tutorials.shape.host --ecc \
--fullchain-file /etc/nginx/ssl/tutorials.shape.host/fullchain.cer \
--key-file /etc/nginx/ssl/tutorials.shape.host/tutorials.shape.host.key \
--reloadcmd "systemctl reload nginx"
cat > /etc/nginx/conf.d/tutorials.shape.host.conf <<'EOF'
server {
listen 80;
server_name tutorials.shape.host;
location /.well-known/acme-challenge/ {
root /var/www/_letsencrypt;
default_type "text/plain";
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl;
http2 on;
server_name tutorials.shape.host;
ssl_certificate /etc/nginx/ssl/tutorials.shape.host/fullchain.cer;
ssl_certificate_key /etc/nginx/ssl/tutorials.shape.host/tutorials.shape.host.key;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
client_max_body_size 0;
proxy_buffering off;
proxy_request_buffering off;
location / {
proxy_pass http://127.0.0.1:9001;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $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;
}
}
EOF
nginx -t
systemctl reload nginx
On the validated AlmaLinux 10.1 run, firewalld allowed the public services, Nginx accepted the configuration cleanly, and ZeroSSL issued a trusted ECC certificate for tutorials.shape.host.

6. Validate the Public HTTPS MinIO Console
Finish by confirming that MinIO is still running under systemd, the local API health endpoint stays healthy, the firewall and SELinux state look correct, and the public HTTPS Console route loads through Nginx with the expected ZeroSSL certificate.
systemctl status --no-pager minio | sed -n '1,12p'
ss -lntp | grep -E ':80|:443|:9000|:9001'
firewall-cmd --list-services
echo "SELinux status: $(getenforce)"
getsebool httpd_can_network_connect || true
minio --version | head -n 1
curl -I http://127.0.0.1:9000/minio/health/live
curl -I --resolve tutorials.shape.host:443:51.89.69.216 https://tutorials.shape.host/login
openssl x509 -in /etc/nginx/ssl/tutorials.shape.host/fullchain.cer -noout -issuer -subject
On the live server, MinIO stayed active under systemd, the local API health endpoint answered successfully, the public Console route loaded over HTTPS, and the installed certificate issuer resolved to ZeroSSL ECC Domain Secure Site CA.

7. Confirm the MinIO Console Login Page in a Browser
The final browser check confirms that the public HTTPS route renders the MinIO Console login page instead of a blank page, proxy error, or certificate warning.

Hardening Notes
- Move the MinIO API off localhost only if you truly need public S3 access. Keeping it local reduces the exposed surface.
- Replace the generated root credentials with values stored in your own secret-management workflow if you plan to keep this node long term.
- Back up both
/mnt/dataand the configuration files under/etc/default/minioand/etc/minio/config.env. - If your host runs SELinux in enforcing mode, keep
httpd_can_network_connectenabled so Nginx can continue proxying to the local Console port.
Conclusion
You now have MinIO running on AlmaLinux 10.1 with the current community release built from source, managed by the official MinIO systemd template, and published through Nginx with a trusted ZeroSSL certificate on tutorials.shape.host. The final live validation confirms the local API health endpoint stays healthy, the Console loads over HTTPS, and the browser reaches the MinIO login screen successfully.