S3Proxy
Overview
This guide covers the deployment and configuration of S3Proxy on Ubuntu 24.04 on Azure using SecureAnyCloud 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 with 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). 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 you get:
- S3Proxy 3.3.0 from the official release, run under systemd as the unprivileged
s3proxyuser (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), withs3proxy.authorization=aws-v2-or-v4so every request is SigV4 authenticated - AWS CLI v2 preinstalled so you can create buckets and read and write objects from the VM immediately
Prerequisites
Before you deploy this image, make sure you have:
- An active Azure subscription, an SSH public key, and a VNet and subnet in the target region
- A subscription to the S3Proxy listing on Azure Marketplace
- Network security group rules that allow 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
/healthzload 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.
Deploy from the Azure Portal
Launch the image from the Azure Marketplace listing. Choose 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. The equivalent Azure CLI commands are:
RG="s3proxy-prod"; LOCATION="eastus"; VM_NAME="s3proxy1" IMAGE_URN="sac1647283583153:s3proxy-ubuntu-24-04:default:latest" SSH_KEY="$(cat ~/.ssh/id_rsa.pub)" az group create --name "$RG" --location "$LOCATION"
# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:s3proxy-ubuntu-24-04:default:latest
az vm create \
--resource-group "$RG" --name "$VM_NAME" \
--image "$IMAGE_URN" \
--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 1002Retrieve 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.txtThe file contains 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.
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'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. It creates a bucket, uploads an object, lists it, then downloads it and prints 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 sac" > /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 --forceAuthentication 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)'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 -1Connecting 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 lsrclone 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-pagerThe 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 SecureAnyCloud deployment includes 24/7 support. Contact the SecureAnyCloud team for help with configuration, backends, scaling or integration.
Deploy on Azure
Launch S3Proxy 3.3.0 on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.
View this image
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com
Overview
This image runs S3Proxy, an open source Java application that implements the Amazon S3 API and proxies requests to a storage backend. It uses the pinned upstream release s3proxy version 3.3.0 (github.com/gaul/s3proxy, Apache License 2.0). The appliance configures the local filesystem backend (the Apache jclouds filesystem provider), so a plain instance and its dedicated EBS data volume become an Amazon S3-compatible object storage endpoint. Point any S3 SDK, the AWS CLI, s3cmd, rclone, restic or a backup tool at it and read and write buckets and objects exactly as you would against Amazon S3, entirely within your own account.
The appliance is built defensively, so it is never an open, anonymous endpoint.
What you get:
- The S3 gateway binds to loopback
127.0.0.1:8080only and is never directly internet-exposed. - An nginx front end terminates TLS on port 443 with a per-instance self-signed certificate and reverse-proxies SigV4-signed requests to the gateway. The client's signed
Hostheader is preserved so signature validation succeeds through the proxy. - Port 80 issues a
301redirect to HTTPS and serves an unauthenticated/healthzendpoint for load balancer probes. - A unique access key and secret key generated on the first boot of every instance, with no credentials baked into the image.
- A dedicated 30 GiB EBS data volume mounted at /var/lib/s3proxy for bucket and object data.
S3Proxy can run with authorization disabled, but this appliance sets s3proxy.authorization=aws-v2-or-v4, so every request must be AWS Signature Version 4 (or Version 2) signed and there is no anonymous mode. On the first boot of every deployed instance, a one-shot service generates a unique access key and secret key, writes them into the gateway configuration and into /root/s3proxy-info.txt with file mode 0600. The gateway service is configured to refuse to start until a key pair is present. No credentials are baked into the image.
Bucket and object data lives on a dedicated 30 GiB EBS data volume mounted at /var/lib/s3proxy, captured into the AMI so every instance is re-provisioned with the same layout. The gateway jar and JRE stay on the OS disk.
Prerequisites
Before you deploy this image you need:
- An Amazon Web Services account where you can launch EC2 instances
- IAM permissions to launch instances, create security groups, and subscribe to AWS Marketplace products
- An EC2 key pair in the target Region for SSH access to the instance
- A VPC and subnet in the target Region, with a security group allowing inbound port 22 from your management network
- The AWS CLI version 2 installed locally if you plan to deploy from the command line or connect an S3 client remotely
Launch the instance from AWS Marketplace
Sign in to the AWS Management Console, open the EC2 service, and select Launch instance. Under Application and OS Images, choose AWS Marketplace AMIs and search for S3Proxy. Select the SecureAnyCloud listing and choose Select, then Continue on the subscription summary.
Pick an instance type of m5.large or larger as a balanced default. Choose your EC2 key pair under Key pair (login). Under Network settings select your VPC and subnet, and create or select a security group that allows inbound port 22 from your management network. To let S3 clients reach the endpoint from other machines, additionally open inbound TCP 443 restricted to your application or client subnets, and optionally 80 for the health probe. Leave the root volume at the default size or larger.
Select Launch instance. First-boot initialization, which generates the per-instance key pair and TLS certificate, takes only a few seconds after the instance state becomes Running and the status checks pass.
Launch the instance with the AWS CLI
The following block launches an instance from the SecureAnyCloud S3Proxy Marketplace AMI into an existing subnet and security group. Replace <ami-id> with the AMI ID shown on the Marketplace listing, <key-name> with your EC2 key pair name, <subnet-id> with your subnet ID, and <security-group-id> with a security group that opens port 22 from your management network.
aws ec2 run-instances \
--image-id <ami-id> \
--instance-type m5.large \
--key-name <key-name> \
--subnet-id <subnet-id> \
--security-group-ids <security-group-id> \
--block-device-mappings '{"DeviceName":"/dev/sda1","Ebs":{"VolumeSize":30,"VolumeType":"gp3"}}' \
--metadata-options 'HttpTokens=required,HttpEndpoint=enabled' \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=s3proxy}]'The AMI also carries a second 30 GiB volume definition for /var/lib/s3proxy. It is attached automatically from the AMI's block device mapping.
Connect to the instance over SSH
Connect to the instance with SSH as the default login user for the operating system variant you launched. The login user differs by variant:
Operating system variant SSH login user
Ubuntu 24.04 LTS ubuntu
ssh -i /path/to/your-key.pem ubuntu@<instance-public-ip>Confirm S3Proxy is running
S3Proxy runs under systemd as s3proxy.service, with nginx fronting it. Confirm both are active:
systemctl is-active s3proxy nginxExpected output:
active activeThe gateway is bound to loopback only, while nginx owns the public TLS port. You can see the split with ss:
sudo ss -tlnp | grep -E ':8080|:443'You will see S3Proxy listening on 127.0.0.1:8080 (shown as [::ffff:127.0.0.1]:8080) and nginx listening on 0.0.0.0:443. The unauthenticated health endpoint answers over TLS:
curl -ks -o /dev/null -w '%{http_code}\n' https://127.0.0.1/healthzIt returns 200.
Read the per-instance access key and secret key
The S3 credentials are generated uniquely on first boot and written to a root-only file. Read them with sudo:
sudo cat /root/s3proxy-info.txtThe file contains S3PROXY_ACCESS_KEY, S3PROXY_SECRET_KEY and S3PROXY_ENDPOINT_URL. Store the secret key somewhere safe. It is the only credential that authenticates requests to your endpoint.
Confirm SigV4 is enforced
There is no anonymous access. An unsigned request is rejected with 403:
curl -ks -o /dev/null -w '%{http_code}\n' https://127.0.0.1/It returns 403, confirming that every request must be SigV4-signed with your per-instance key pair.
Run an S3 object round trip
From the instance, export your per-instance key pair and run a full create-bucket, upload, list and read-back cycle against the endpoint. The certificate is self-signed per instance, so pass --no-verify-ssl (or trust the certificate). S3Proxy uses path-style addressing because the endpoint is an IP address.
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 aws --no-verify-ssl --endpoint-url https://127.0.0.1/ s3 mb s3://my-first-bucket echo "hello from s3proxy" > /tmp/hello.txt aws --no-verify-ssl --endpoint-url https://127.0.0.1/ s3 cp /tmp/hello.txt s3://my-first-bucket/ aws --no-verify-ssl --endpoint-url https://127.0.0.1/ s3 ls s3://my-first-bucket/ aws --no-verify-ssl --endpoint-url https://127.0.0.1/ s3 cp s3://my-first-bucket/hello.txt - aws --no-verify-ssl --endpoint-url https://127.0.0.1/ s3 rb s3://my-first-bucket --forceThe commands above read your per-instance key pair straight from /root/s3proxy-info.txt, so you can copy and paste them on the instance. The object round trips through the gateway and is stored on the data volume under /var/lib/s3proxy/data.
Connect a remote S3 client
From any machine that can reach the instance on port 443, point the AWS CLI (or boto3, s3cmd, rclone or restic) at https://<instance-public-ip>/ with the same per-instance key pair. Because the certificate is self-signed, either trust /etc/nginx/tls/s3proxy.crt on the client or pass the tool's insecure flag while testing:
export AWS_ACCESS_KEY_ID=<your-access-key>
export AWS_SECRET_ACCESS_KEY=<your-secret-key>
export AWS_DEFAULT_REGION=us-east-1 aws --no-verify-ssl --endpoint-url https://<instance-public-ip>/ s3 lsIn boto3, pass endpoint_url='https://<instance-public-ip>/', aws_access_key_id, aws_secret_access_key, and config=boto3.session.Config(s3={'addressing_style': 'path'}). For production, front the appliance with your own domain and a CA-signed certificate.
Network security model
Every request on port 443 is SigV4-authenticated, but network isolation is still your first line of defense. The shipped default security group opens only port 22, so port 443 (and 80) are not reachable from off the instance until you open them deliberately. When you do:
- Open 443 only to the application or client subnets that need to read and write objects. Restrict it to known CIDR ranges rather than
0.0.0.0/0. - Optionally open 80 only where a load balancer needs the unauthenticated
/healthzprobe. All other port 80 traffic is redirected to HTTPS. - Keep port 22 restricted to your management network.
Because the secret key is transmitted only inside a SigV4 signature and TLS protects the transport, keys never cross the wire in plaintext. Rotate the key pair at any time by editing s3proxy.identity and s3proxy.credential in /etc/s3proxy/s3proxy.conf and running sudo systemctl restart s3proxy.
Maintenance
Keep the operating system patched with the standard package manager, for example sudo apt-get update && sudo apt-get -y upgrade on Ubuntu, and reboot when a new kernel is installed.
Bucket and object data persists on the dedicated EBS volume at /var/lib/s3proxy across reboots; take regular EBS snapshots of that volume for backups. Review the nginx access and error logs under /var/log/nginx/ for client activity. The S3Proxy service logs are available with sudo journalctl -u s3proxy.
Screenshots
S3Proxy running under systemd, with the gateway bound to loopback, nginx terminating TLS on 443, and the health endpoint returning 200:
A full S3 object round trip through aws-cli against the S3Proxy endpoint over TLS, returning the exact object content:
SigV4 enforcement: an unsigned request is refused with 403, a wrong secret key is denied, and the per-instance key pair is accepted:
Support
SecureAnyCloud provides 24/7 technical support for this product by email at sales@secureanycloud.com and by live chat. Our engineers can help with deployment, pointing the AWS CLI, boto3, s3cmd, rclone and restic at the S3Proxy endpoint, security group and network isolation guidance for the S3 API port, TLS termination with your own domain and certificate, and filesystem-backend and data-volume sizing. For critical issues we target a one-hour average response, and general inquiries are handled during the same business day.
Deploy on AWS
Launch S3Proxy on AWS with 24/7 support from SecureAnyCloud.
View on Marketplace
Need Help?
Our support team is available 24/7.
sales@secureanycloud.com