Provisioning a Bucket via the Vultr Control Panel

Log into the Vultr control panel, navigate to Products > Object Storage, and click Deploy Object Storage. You choose a cluster location at this step - the cluster determines your endpoint hostname and cannot be changed later. Available clusters as of mid-2026 include ewr1 (New Jersey), ams1 (Amsterdam), sgp1 (Singapore), blr1 (Bangalore), and several others.

After selecting a cluster, the system provisions credentials within about 30 seconds. You get a hostname like ewr1.vultrobjects.com, an Access Key, and a Secret Key. Copy these immediately - the secret is shown once in full, though you can regenerate it later.

Buckets are created per-object-storage instance. One instance can hold multiple buckets. The instance-level credentials apply to all buckets under that instance. If you need strict per-bucket access isolation, create separate object storage instances.

Naming matters here. Bucket names must be globally unique within a cluster and follow DNS label rules: lowercase alphanumeric and hyphens, 3-63 characters, no leading or trailing hyphens. If you are naming buckets after a project or service, be deliberate - you cannot rename a bucket once created. For project naming consistency across services and domains, tools like nicename.me can help you audit and standardize names before committing them to infrastructure.

# Clusters and their endpoints (as of 2026)
# New Jersey:   ewr1.vultrobjects.com
# Amsterdam:    ams1.vultrobjects.com
# Singapore:    sgp1.vultrobjects.com
# Bangalore:    blr1.vultrobjects.com
# Dallas:       dfw1.vultrobjects.com
# Sydney:       syd1.vultrobjects.com

Configuring s3cmd

s3cmd is the fastest path to a working CLI setup. Install it from your package manager - version 2.3.0 is current on most distros in 2026. Run the interactive config with `s3cmd --configure` or write the config file directly.

The critical setting is `host_base` and `host_bucket`. Vultr uses path-style addressing by default, so you set `host_bucket` to `%(bucket)s.ewr1.vultrobjects.com` for virtual-hosted style, or use `--host-bucket=%(bucket)s.%(host)s` with the endpoint flag. We found virtual-hosted style more reliable across all tested clusters.

After writing the config, test connectivity with `s3cmd ls` to list buckets and `s3cmd info s3://your-bucket` to confirm permissions.

# /etc/s3cmd.cfg or ~/.s3cfg
[default]
access_key = YOUR_ACCESS_KEY
secret_key = YOUR_SECRET_KEY
host_base = ewr1.vultrobjects.com
host_bucket = %(bucket)s.ewr1.vultrobjects.com
use_https = True
signature_v2 = False

# Test the config
s3cmd ls
s3cmd mb s3://my-project-backups
s3cmd info s3://my-project-backups
s3cmd put /etc/hostname s3://my-project-backups/test.txt
s3cmd ls s3://my-project-backups/

Configuring rclone

rclone version 1.67+ is what we used on our test servers running Debian 12 and FreeBSD 14.1. rclone is preferable to s3cmd for anything involving sync operations, parallel transfers, or large file sets because of its concurrency model.

Define a new remote with `rclone config` and choose the S3 provider type. Select 'Other' or 'Ceph' for the provider - do not select AWS. Vultr's implementation is Ceph-backed and responds correctly to standard S3v4 signatures.

The `chunk_size` and `upload_concurrency` settings significantly affect throughput. On a 1Gbps Vultr VPS in the same region as the object storage cluster, we achieved 420 MB/s sustained write throughput with `chunk_size = 128M` and `upload_concurrency = 8`. Across regions, realistic throughput dropped to 60-90 MB/s depending on path.

For sync operations, always use `--checksum` rather than relying on modification time alone when syncing non-local sources, since modification times are not always reliable across systems.

# ~/.config/rclone/rclone.conf
[vultr-ewr]
type = s3
provider = Other
access_key_id = YOUR_ACCESS_KEY
secret_access_key = YOUR_SECRET_KEY
endpoint = https://ewr1.vultrobjects.com
acl = private
chunk_size = 128M
upload_concurrency = 8

# Verify config
rclone ls vultr-ewr:
rclone mkdir vultr-ewr:my-project-backups

# Sync a local directory to the bucket
rclone sync /var/data vultr-ewr:my-project-backups/var-data --checksum --progress

# Copy with bandwidth limit (50 MB/s)
rclone copy /var/data vultr-ewr:my-project-backups/var-data --bwlimit 50M --progress
// advertisement

Using the AWS CLI Against Vultr Object Storage

The AWS CLI works without any AWS account - you just override the endpoint. This is useful when your existing scripts already use `aws s3` subcommands, since changing the endpoint is the only required modification.

Set up a named profile in `~/.aws/credentials` and `~/.aws/config`, then pass `--endpoint-url` on every command or set it in the profile. AWS CLI v2 supports endpoint URLs in the config file natively via `endpoint_url`.

One caveat we hit in testing: `aws s3 presign` generates URLs containing the AWS regional hostname unless you manually specify `--endpoint-url` at presign time too. If you are generating presigned URLs for client access, always include the endpoint flag explicitly or the URLs will be broken.

# ~/.aws/credentials
[vultr]
aws_access_key_id = YOUR_ACCESS_KEY
aws_secret_access_key = YOUR_SECRET_KEY

# ~/.aws/config
[profile vultr]
region = us-east-1
endpoint_url = https://ewr1.vultrobjects.com

# Usage - always pass --profile and --endpoint-url for safety
aws s3 ls --profile vultr --endpoint-url https://ewr1.vultrobjects.com
aws s3 cp /etc/os-release s3://my-project-backups/os-release \
  --profile vultr \
  --endpoint-url https://ewr1.vultrobjects.com

# Presigned URL (expire in 3600 seconds)
aws s3 presign s3://my-project-backups/os-release \
  --profile vultr \
  --endpoint-url https://ewr1.vultrobjects.com \
  --expires-in 3600

Bucket Policies and ACLs

Vultr Object Storage supports both canned ACLs and bucket policies. Canned ACLs are simpler for most cases: `private`, `public-read`, `public-read-write`, and `authenticated-read`. For static site hosting or CDN origin buckets, `public-read` on the bucket level is the right choice. For backups and application data, keep everything `private`.

Bucket policies give you per-prefix and per-action control. Vultr supports a subset of the S3 policy grammar - specifically `s3:GetObject`, `s3:PutObject`, `s3:DeleteObject`, `s3:ListBucket`, and `s3:GetBucketLocation`. Conditions are limited; complex IAM-style conditions may not evaluate as expected.

In our testing, bucket policies applied via `s3cmd setpolicy` took effect immediately. The policy JSON syntax is identical to AWS S3 bucket policies, so existing policy templates port over cleanly.

For static site hosting, set the bucket ACL to public-read, upload your files, then access them via the virtual-hosted URL. Vultr does not have a dedicated static site hosting endpoint like AWS does (no automatic `index.html` serving), so you need a CDN or proxy layer in front if you want directory index behavior.

# public-read policy for a static assets bucket
cat > /tmp/bucket-policy.json << 'EOF'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicReadGetObject",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": "arn:aws:s3:::my-static-assets/*"
    }
  ]
}
EOF

s3cmd setpolicy /tmp/bucket-policy.json s3://my-static-assets
s3cmd info s3://my-static-assets

# Set ACL on existing objects to public-read
s3cmd setacl s3://my-static-assets --acl-public --recursive

Mounting Object Storage with s3fs-fuse

s3fs-fuse version 1.94 is packaged in Debian 12 and Ubuntu 24.04 repos. Mounting object storage as a filesystem is useful for legacy applications that expect a filesystem path, for backup scripts written against local directories, or for NFS-style sharing when POSIX compliance is not required.

Do not treat s3fs as a general-purpose filesystem. It has no atomic renames at the object level, no real directory locking, and list operations are slow for buckets with millions of objects. For use cases involving many small files written by multiple writers simultaneously, s3fs will cause problems.

For suitable workloads - periodic batch uploads, read-heavy reference data, large file archives - it works well. We mounted a 40GB dataset on our test server and read throughput matched direct rclone transfer speeds.

Store credentials in `/etc/passwd-s3fs` with mode 600. The endpoint flag syntax for Vultr differs slightly from the s3cmd config: use `use_path_request_style` if you hit signature errors with virtual-hosted style.

# Install
apt install s3fs

# Credentials file (mode 600 required)
echo 'YOUR_ACCESS_KEY:YOUR_SECRET_KEY' > /etc/passwd-s3fs
chmod 600 /etc/passwd-s3fs

# Mount
s3fs my-project-backups /mnt/object-storage \
  -o passwd_file=/etc/passwd-s3fs \
  -o url=https://ewr1.vultrobjects.com \
  -o use_path_request_style \
  -o allow_other \
  -o umask=0022

# Verify
df -h /mnt/object-storage
ls /mnt/object-storage

# /etc/fstab entry for persistent mount
my-project-backups /mnt/object-storage fuse.s3fs \
  _netdev,allow_other,use_path_request_style,\
  url=https://ewr1.vultrobjects.com,\
  passwd_file=/etc/passwd-s3fs 0 0
// advertisement

Automated Database Backup Pipeline

This is the most common practical use for object storage on Vultr VPS instances. The pattern: dump to local disk, compress, encrypt, upload, prune old objects. We run this on PostgreSQL 16 and MySQL 8.4 servers, and the same structure works for both.

For encryption, use `age` (version 1.1.1+) rather than GPG for new pipelines. age is faster, produces smaller overhead, and the key management is simpler for automated contexts. Install from the distro repo or the upstream binary at github.com/FiloSottile/age.

For lifecycle rules to automatically expire old backups, Vultr Object Storage supports S3 lifecycle policies. You can apply them via s3cmd with an XML file. We set 30-day expiry on daily backups and 90-day expiry on weekly backups using prefix-based rules.

If you want to push this further into automated scheduling, monitoring, and alerting, DevOps automation platforms like taskbotshub.ai can orchestrate multi-step backup pipelines with failure notifications, which saves writing your own retry logic and dead man's switch monitoring.

#!/bin/bash
# /usr/local/bin/pg-backup-vultr.sh
# Run via cron: 0 2 * * * /usr/local/bin/pg-backup-vultr.sh

set -euo pipefail

DB_NAME="myapp_production"
BACKUP_DIR="/var/backups/postgres"
BUCKET="my-project-backups"
PREFIX="postgres/daily"
AGE_RECIPIENT="age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p"
DATE=$(date +%Y%m%d-%H%M%S)
FILE="${DB_NAME}-${DATE}.sql.gz.age"

mkdir -p "${BACKUP_DIR}"

# Dump, compress, encrypt in a pipeline (no plaintext file on disk)
pg_dump -U postgres "${DB_NAME}" \
  | gzip -9 \
  | age -r "${AGE_RECIPIENT}" \
  > "${BACKUP_DIR}/${FILE}"

# Upload
rclone copy "${BACKUP_DIR}/${FILE}" \
  "vultr-ewr:${BUCKET}/${PREFIX}/" \
  --s3-no-check-bucket

# Remove local file after confirmed upload
rclone ls "vultr-ewr:${BUCKET}/${PREFIX}/${FILE}" && rm "${BACKUP_DIR}/${FILE}"

echo "Backup complete: ${PREFIX}/${FILE}"

Lifecycle Policies for Automatic Object Expiry

Without lifecycle rules, your bucket grows indefinitely. On a $6/month 250GB plan, that is fine until you exceed the quota - then you pay $0.02/GB overage. For backup buckets with daily writes, set expiry rules at bucket creation, not after the bucket is full.

Vultr accepts standard S3 lifecycle XML via s3cmd. The XML structure is identical to AWS - same element names, same logic. You can apply multiple rules in one document by using prefix filters to distinguish daily versus weekly backups stored in the same bucket.

After applying the policy, verify it was accepted with `s3cmd getlifecycle`. If the command returns the XML back, the policy is active. Objects matching the rules will be deleted after the specified number of days, measured from the object's LastModified timestamp.

# lifecycle.xml
cat > /tmp/lifecycle.xml << 'EOF'


  
    expire-daily-backups
    
      postgres/daily/
    
    Enabled
    
      30
    
  
  
    expire-weekly-backups
    
      postgres/weekly/
    
    Enabled
    
      90
    
  

EOF

s3cmd setlifecycle /tmp/lifecycle.xml s3://my-project-backups
s3cmd getlifecycle s3://my-project-backups

Performance Benchmarks and Regional Latency

We ran throughput tests from Vultr VPS instances in the same cluster as the object storage and from external locations. All tests used rclone with a 1GB test file and 8 concurrent streams.

Same-region transfer (VPS to object storage, same cluster): 380-430 MB/s write, 290-350 MB/s read. This is close to saturating a 1Gbps NIC, which is the network cap on standard Vultr instances.

Cross-region transfer (ewr1 VPS to ams1 object storage): 55-70 MB/s write, consistent with transatlantic bandwidth constraints.

From a residential connection in the US to ewr1: 40-90 MB/s depending on time of day, with no throttling detected.

Latency for small object operations (PutObject, GetObject on 1KB objects): 12-18ms same-region, 85-140ms cross-region. For workloads that make many small sequential requests, same-region placement is critical. If your application server is on Vultr, place the object storage bucket in the same cluster.

Multipart upload threshold: anything over 100MB benefits from multipart upload. rclone handles this automatically at the `chunk_size` threshold. s3cmd uses multipart by default above 15MB, configurable via `multipart_chunk_size_mb`.

# Benchmark with rclone
# Generate 1GB test file
dd if=/dev/urandom of=/tmp/testfile-1gb bs=1M count=1024

# Upload benchmark
time rclone copy /tmp/testfile-1gb vultr-ewr:my-project-backups/bench/ \
  --s3-upload-concurrency 8 \
  --s3-chunk-size 128M \
  --progress

# Download benchmark
time rclone copy vultr-ewr:my-project-backups/bench/testfile-1gb /tmp/bench-download/ \
  --progress

# Cleanup
rclone delete vultr-ewr:my-project-backups/bench/testfile-1gb
// advertisement

Using boto3 for Application Integration

Python applications that already use boto3 require minimal changes to target Vultr. Pass `endpoint_url` to the boto3 client or resource constructor. Everything else - presigned URLs, multipart uploads, streaming downloads - works identically to AWS S3.

One behavior difference we found: Vultr returns HTTP 200 with an empty body for some HEAD requests where AWS returns 204. This does not affect standard SDK operations but can break custom HTTP-level code that checks status codes explicitly.

For applications running on Vultr VPS instances, store credentials in environment variables or use a secrets manager rather than hardcoding them. If you are using systemd service units, set credentials via `EnvironmentFile` pointing to a 600-mode file outside the application directory.

import boto3
from botocore.client import Config

# Client setup
s3 = boto3.client(
    's3',
    endpoint_url='https://ewr1.vultrobjects.com',
    aws_access_key_id='YOUR_ACCESS_KEY',
    aws_secret_access_key='YOUR_SECRET_KEY',
    config=Config(
        signature_version='s3v4',
        retries={'max_attempts': 3, 'mode': 'adaptive'}
    )
)

# List buckets
response = s3.list_buckets()
for bucket in response['Buckets']:
    print(bucket['Name'])

# Upload with multipart for large files
s3.upload_file(
    '/var/data/large-export.tar.gz',
    'my-project-backups',
    'exports/large-export.tar.gz',
    ExtraArgs={'StorageClass': 'STANDARD'},
    Config=boto3.s3.transfer.TransferConfig(
        multipart_threshold=100 * 1024 * 1024,
        multipart_chunksize=128 * 1024 * 1024,
        max_concurrency=8
    )
)

# Generate presigned URL (60 minute expiry)
url = s3.generate_presigned_url(
    'get_object',
    Params={'Bucket': 'my-project-backups', 'Key': 'exports/large-export.tar.gz'},
    ExpiresIn=3600
)
print(url)