S3
Storage Azure

S3Proxy on Ubuntu 24.04 on Azure User Guide

| Product: S3Proxy 3.3.0 on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and configuration of S3Proxy on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. S3Proxy is an open source Java application that implements the Amazon S3 API and proxies requests to a storage backend. This image configures the local filesystem backend, so a plain virtual machine and its dedicated data disk become an S3 compatible object storage endpoint: point any S3 SDK, the AWS CLI, s3cmd, rclone or a backup tool at it and read and write buckets and objects exactly as you would against Amazon S3, entirely within your own subscription.

The image installs S3Proxy 3.3.0 from the official release, run under systemd as an unprivileged s3proxy user on the Java 17 runtime. The gateway binds to loopback only and is exposed through an nginx reverse proxy that terminates TLS on port 443, so the S3 API is reachable securely over HTTPS. It is a headless service: there is no web interface, so you operate it with any standard S3 client. The AWS CLI v2 ships in the image so you can test immediately on the VM.

Secure by default — SigV4 required, no default credentials. S3Proxy can be run with authorization disabled, which serves an open, anonymous S3 endpoint. This image closes that gap: it sets s3proxy.authorization=aws-v2-or-v4, so every request must be AWS Signature Version 2 or 4 signed. On the very first boot of every VM it generates a unique access key and secret key, writes them into the gateway configuration and records them in /root/s3proxy-info.txt (mode 0600, root only), and the service is configured to refuse to start until a key pair is present. As a result an unsigned or wrong key request is rejected, and no two instances share a credential.

What is included:

  • S3Proxy 3.3.0 from the official release, run under systemd as the unprivileged s3proxy user (s3proxy.service) on OpenJDK 17

  • An HTTPS S3 API endpoint on port 443, terminated by nginx with a self signed certificate generated per virtual machine on first boot

  • The local filesystem backend (Apache jclouds), storing buckets and objects on a dedicated data disk mounted at /var/lib/s3proxy

  • A unique access key and secret key generated on first boot and documented in /root/s3proxy-info.txt (0600), with s3proxy.authorization=aws-v2-or-v4 so every request is SigV4 authenticated

  • AWS CLI v2 preinstalled so you can create buckets and read and write objects from the VM immediately

Prerequisites

  • Active Azure subscription, SSH public key, VNet + subnet in target region

  • Subscription to the S3Proxy listing on Azure Marketplace

  • Network Security Group rules allowing TCP 22 (admin) and TCP 443 (the HTTPS S3 API) from the client networks that will use the endpoint. Port 80 answers a redirect to HTTPS and an unauthenticated /healthz load balancer probe

Recommended virtual machine size: Standard_B2s (2 vCPU, 4 GB RAM) is ample for development and small production workloads; the gateway runs with a 1024 MB JVM heap on this size. Scale to a larger size, or attach a larger data disk, for higher throughput or capacity.

Step 1: Deploy from the Azure Portal

Launch the image from the Azure Marketplace listing, choosing your resource group, region, size (Standard_B2s or larger) and SSH key. Attach the offer's data disk and open ports 22 and 443 in the Network Security Group. Equivalent Azure CLI:

RG="s3proxy-prod"; LOCATION="eastus"; VM_NAME="s3proxy1"
GALLERY_IMAGE_ID="/subscriptions/<sub-id>/resourceGroups/azure-cloudimg/providers/Microsoft.Compute/galleries/cloudimgGallery/images/s3proxy-ubuntu-24-04/versions/<version>"
SSH_KEY="$(cat ~/.ssh/id_rsa.pub)"
az group create --name "$RG" --location "$LOCATION"
az vm create \
  --resource-group "$RG" --name "$VM_NAME" \
  --image "$GALLERY_IMAGE_ID" \
  --size Standard_B2s \
  --admin-username azureuser \
  --ssh-key-values "$SSH_KEY" \
  --public-ip-sku Standard
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 443 --priority 1001
az vm open-port --resource-group "$RG" --name "$VM_NAME" --port 22 --priority 1002

Step 2: Retrieve your per-VM S3 credentials

On first boot the VM generates a unique access key and secret key and writes them to a root only file. SSH in as azureuser and read them:

sudo cat /root/s3proxy-info.txt

You will see S3PROXY_ACCESS_KEY, S3PROXY_SECRET_KEY and S3PROXY_ENDPOINT_URL (the HTTPS endpoint using this VM's public IP). These keys are unique to this VM and were not baked into the image. Keep this file secret.

Step 3: Verify the service is running

Confirm the gateway, the Java runtime and the network listeners. S3Proxy binds to loopback (127.0.0.1:8080); nginx binds the public HTTPS endpoint on 0.0.0.0:443:

systemctl is-active s3proxy.service nginx.service
java -version
ss -tlnH | grep -E ':8080|:443'

s3proxy.service and nginx.service both report active, java reports OpenJDK 17, and ss shows s3proxy listening on loopback 127.0.0.1 port 8080 while nginx listens on 0.0.0.0 port 443

Step 4: Create a bucket and store objects

The AWS CLI ships in the image. Because the endpoint is an IP address with a self signed certificate, use --endpoint-url and --no-verify-ssl. The following block reads this VM's own keys and runs a full round trip against the local endpoint: create a bucket, upload an object, list it, then download it and print its contents.

export AWS_ACCESS_KEY_ID=$(sudo grep '^S3PROXY_ACCESS_KEY=' /root/s3proxy-info.txt | cut -d= -f2-)
export AWS_SECRET_ACCESS_KEY=$(sudo grep '^S3PROXY_SECRET_KEY=' /root/s3proxy-info.txt | cut -d= -f2-)
export AWS_DEFAULT_REGION=us-east-1
echo "hello from cloudimg" > /tmp/demo.txt
aws --no-verify-ssl --endpoint-url https://127.0.0.1/ s3 mb s3://demo-bucket
aws --no-verify-ssl --endpoint-url https://127.0.0.1/ s3 cp /tmp/demo.txt s3://demo-bucket/demo.txt
aws --no-verify-ssl --endpoint-url https://127.0.0.1/ s3 ls s3://demo-bucket/
aws --no-verify-ssl --endpoint-url https://127.0.0.1/ s3 cp s3://demo-bucket/demo.txt -
aws --no-verify-ssl --endpoint-url https://127.0.0.1/ s3 rb s3://demo-bucket --force

the AWS CLI creates demo-bucket, uploads demo.txt, lists the bucket showing the 20 byte object, then downloads it and prints hello from cloudimg, all over the appliance HTTPS endpoint with the per VM key

Step 5: Authentication is enforced

There is no anonymous access. An unsigned request is rejected with HTTP 403, and a request signed with the wrong secret key is rejected with SignatureDoesNotMatch:

curl -ks -o /dev/null -w 'unsigned request: HTTP %{http_code}\n' https://127.0.0.1/
AK=$(sudo grep '^S3PROXY_ACCESS_KEY=' /root/s3proxy-info.txt | cut -d= -f2-)
AWS_ACCESS_KEY_ID=$AK AWS_SECRET_ACCESS_KEY=wrong-secret AWS_DEFAULT_REGION=us-east-1 \
  aws --no-verify-ssl --endpoint-url https://127.0.0.1/ s3 ls >/dev/null 2>&1 \
  && echo 'UNEXPECTED: wrong key accepted' \
  || echo 'wrong key rejected (expected, SignatureDoesNotMatch)'

an unsigned curl request to the endpoint returns HTTP 403, and an AWS CLI request signed with a wrong secret key is rejected with SignatureDoesNotMatch, proving only requests signed with the per VM key are authorised

Step 6: Security posture and where your data lives

The credentials file is root only (0600), s3proxy.authorization is aws-v2-or-v4, and bucket and object data lives on the dedicated data disk mounted at /var/lib/s3proxy:

sudo stat -c '%n %a %U:%G' /root/s3proxy-info.txt
sudo grep '^s3proxy.authorization=' /etc/s3proxy/s3proxy.conf
df -h /var/lib/s3proxy | tail -1

the credentials file is 0600 root root, the gateway configuration shows authorization aws-v2-or-v4, and df shows the object store data directory mounted on the dedicated 30 GB data disk at /var/lib/s3proxy

Connecting from a remote client

From your own machine, configure any S3 client with this VM's keys and its public endpoint. With the AWS CLI, configure a named profile and force path style addressing (the endpoint is an IP address):

aws configure set aws_access_key_id <your-access-key> --profile s3proxy
aws configure set aws_secret_access_key <your-secret-key> --profile s3proxy
aws configure set region us-east-1 --profile s3proxy
aws configure set s3.addressing_style path --profile s3proxy
aws --profile s3proxy --endpoint-url https://<vm-ip>/ --no-verify-ssl s3 ls

rclone works the same way, with an S3 remote whose provider = Other, endpoint = https://<vm-ip>/, access_key_id and secret_access_key set to your keys, and force_path_style = true. Any tool or SDK that speaks the S3 API (restic, Veeam, s3cmd, boto3) can target the endpoint unchanged.

Using the TLS certificate

The certificate is self signed and regenerated per virtual machine on first boot, with the VM public IP, hostname and 127.0.0.1 in its subject alternative names. For quick testing pass --no-verify-ssl (AWS CLI) or the equivalent. For production, either distribute the certificate at /etc/nginx/tls/s3proxy.crt to your clients as a trusted CA, or place the appliance behind a load balancer or reverse proxy with a certificate issued by a public CA for your own domain, and point clients at that hostname.

Rotating the access keys

The key pair lives in two lines of the gateway configuration, s3proxy.identity (the access key) and s3proxy.credential (the secret key). To rotate it, set new values, keep /root/s3proxy-info.txt in step, and restart the service. This example generates a fresh random key pair and applies it:

NEW_ACCESS_KEY=$(openssl rand -hex 10 | tr 'a-z' 'A-Z')
NEW_SECRET_KEY=$(openssl rand -base64 48 | tr -dc 'A-Za-z0-9' | head -c 40)
sudo sed -i "s|^s3proxy.identity=.*|s3proxy.identity=$NEW_ACCESS_KEY|" /etc/s3proxy/s3proxy.conf
sudo sed -i "s|^s3proxy.credential=.*|s3proxy.credential=$NEW_SECRET_KEY|" /etc/s3proxy/s3proxy.conf
sudo sed -i "s|^S3PROXY_ACCESS_KEY=.*|S3PROXY_ACCESS_KEY=$NEW_ACCESS_KEY|" /root/s3proxy-info.txt
sudo sed -i "s|^S3PROXY_SECRET_KEY=.*|S3PROXY_SECRET_KEY=$NEW_SECRET_KEY|" /root/s3proxy-info.txt
sudo systemctl restart s3proxy.service
echo "key pair rotated and recorded in /root/s3proxy-info.txt"

Update any clients with the new keys. To set your own values instead, edit /etc/s3proxy/s3proxy.conf directly and restart the service.

Managing the service

systemctl status s3proxy.service nginx.service --no-pager
sudo journalctl -u s3proxy.service -n 50 --no-pager

The gateway restarts automatically on failure and starts on boot. Object data persists on the data disk across restarts and reboots.

Troubleshooting

  • A client reports SignatureDoesNotMatch: confirm the client is using the exact access key and secret key from /root/s3proxy-info.txt and path style addressing.

  • A client reports a certificate error: the certificate is self signed. Trust /etc/nginx/tls/s3proxy.crt, use --no-verify-ssl for testing, or front the appliance with a real certificate as described above.

  • A client cannot connect: confirm the Network Security Group allows TCP 443 from the client network, and that systemctl is-active s3proxy.service nginx.service reports active.

Support

Every cloudimg deployment includes 24/7 support. Contact the cloudimg team for help with configuration, backends, scaling or integration.