Install MinIO on Ubuntu 24.04 with Nginx and ZeroSSL
MinIO gives you a fast, S3-compatible object storage server that works well when you want private buckets, predictable performance, and a lightweight web console on a single VPS without deploying a larger storage stack.
In this guide, we restore a fresh Ubuntu 24.04.1 LTS 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 | Ubuntu 24.04.1 LTS |
| Build toolchain | Go 1.26.1 |
| Reverse proxy | Nginx 1.24.0 |
| Public hostname | tutorials.shape.host |
| TLS issuer | ZeroSSL ECC Domain Secure Site CA |
| Validated on | Live Shape.Host Ubuntu 24.04.1 server |
Why Use MinIO on Ubuntu 24.04?
- Ubuntu 24.04.1 LTS gives you a current long-term support base with familiar package management and a broad ecosystem.
- 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 API and Console ports stay local to the server.
Before You Begin
Make sure the following prerequisites are in place before you start:
- A fresh Ubuntu 24.04 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 Ubuntu 24.04 Release
Start by confirming that the restored server is actually running Ubuntu 24.04.1 LTS.
cat /etc/os-release

2. Install Go, Nginx, UFW, 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 tagged upstream repository metadata, plus Nginx, UFW, and the helper packages needed for certificate automation.
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y curl git tar gzip nginx openssl socat ufw ca-certificates
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
ufw allow OpenSSH
ufw allow 'Nginx Full'
ufw --force enable
go version
nginx -v
ufw status
On the validated Ubuntu 24.04.1 deployment, this installed the official Go 1.26.1 toolchain, Git 2.43.0, Nginx 1.24.0, and an active UFW policy that allowed both SSH and the Nginx HTTPS profile.

3. Build MinIO from Source and Prepare the Official Service Configuration
The current MinIO 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 HTTP/1.1 200 OK, and the local Console login page answered correctly.

5. Configure Nginx, UFW, 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
rm -f /etc/nginx/sites-enabled/default
cat > /etc/nginx/sites-available/tutorials.shape.host <<'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
ln -sfn /etc/nginx/sites-available/tutorials.shape.host /etc/nginx/sites-enabled/tutorials.shape.host
ufw allow 'Nginx Full'
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/sites-available/tutorials.shape.host <<'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;
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 Ubuntu 24.04.1 run, UFW allowed the public web profile, 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 Ubuntu firewall rules 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'
ufw status
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. - Keep the Ubuntu firewall limited to SSH and the Nginx HTTPS profile unless you intentionally need more inbound services.
Conclusion
You now have MinIO running on Ubuntu 24.04.1 LTS 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.