Python SDK Guide
Use the official AWS SDK for Python (Boto3) with RustFS.
Overview
RustFS ships no first-party SDKs — it is S3-compatible, so you use the official AWS SDK for Python, Boto3, configured to point at your RustFS server.
This guide covers:
- Bucket creation/deletion
- Object upload/download/deletion
- Listing objects
- Generating presigned URLs
- Multipart upload for large files
Prerequisites
- Python 3.8 or later
- A running RustFS instance (see the Installation Guide) — the S3 API listens on port
9000, the Console on port9001 - Access keys, set at install time via the
RUSTFS_ACCESS_KEY/RUSTFS_SECRET_KEYenvironment variables (see Access Key Management)
Local test
If you did not set credentials at install time, the server defaults to rustfsadmin / rustfsadmin — fine for a throwaway local trial, never for anything reachable by others.
2.1 Install Boto3
We recommend using a virtual environment:
python3 -m venv venv
source venv/bin/activate
pip install boto3Boto3 depends on
botocore, which will be installed automatically.
Connecting to RustFS
The following is a complete, runnable script. Replace localhost with your server's IP address if RustFS runs on another machine, and fill in your own access keys:
import boto3
from botocore.client import Config
s3 = boto3.client(
's3',
endpoint_url='http://localhost:9000',
aws_access_key_id='<your-access-key>',
aws_secret_access_key='<your-secret-key>',
region_name='us-east-1',
config=Config(
signature_version='s3v4',
s3={'addressing_style': 'path'},
),
)
response = s3.list_buckets()
for bucket in response['Buckets']:
print(bucket['Name'])Run it:
python main.pyClient parameters explained
endpoint_url— points to your RustFS S3 API (port9000, not the Console port9001)signature_version='s3v4'— RustFS supports v4 signaturesregion_name='us-east-1'— RustFS's default regionaddressing_style='path'— RustFS uses path-style URLs by default; virtual-host style requiresRUSTFS_SERVER_DOMAINS
Basic Operations
4.1 Create Bucket
bucket_name = 'my-bucket'
try:
s3.create_bucket(Bucket=bucket_name)
print(f'Bucket {bucket_name} created.')
except s3.exceptions.BucketAlreadyOwnedByYou:
print(f'Bucket {bucket_name} already exists.')Bucket my-bucket created.4.2 Upload File
s3.upload_file('/path/to/hello.txt', bucket_name, 'hello.txt')
print('File uploaded.')File uploaded.4.3 Download File
s3.download_file(bucket_name, 'hello.txt', 'hello-downloaded.txt')
print('File downloaded.')File downloaded.4.4 List Objects
response = s3.list_objects_v2(Bucket=bucket_name)
for obj in response.get('Contents', []):
print(f"- {obj['Key']} ({obj['Size']} bytes)")- hello.txt (12 bytes)4.5 Delete Object and Bucket
s3.delete_object(Bucket=bucket_name, Key='hello.txt')
print('Object deleted.')
s3.delete_bucket(Bucket=bucket_name)
print('Bucket deleted.')Object deleted.
Bucket deleted.Advanced Features
5.1 Generate Presigned URLs
5.1.1 Download Link (GET)
url = s3.generate_presigned_url(
ClientMethod='get_object',
Params={'Bucket': bucket_name, 'Key': 'hello.txt'},
ExpiresIn=600, # 10 minutes validity
)
print('Presigned GET URL:', url)Presigned GET URL: http://localhost:9000/my-bucket/hello.txt?X-Amz-Algorithm=AWS4-HMAC-SHA256&...5.1.2 Upload Link (PUT)
url = s3.generate_presigned_url(
ClientMethod='put_object',
Params={'Bucket': bucket_name, 'Key': 'upload-by-url.txt'},
ExpiresIn=600,
)
print('Presigned PUT URL:', url)You can use the curl tool to upload:
curl -X PUT --upload-file /path/to/hello.txt "http://localhost:9000/my-bucket/upload-by-url.txt?X-Amz-Algorithm=..."5.2 Multipart Upload
Suitable for files larger than 10 MB, allows manual control of each part.
file_path = 'largefile.bin'
key = 'largefile.bin'
part_size = 5 * 1024 * 1024 # 5 MB
# 1. Start upload
response = s3.create_multipart_upload(Bucket=bucket_name, Key=key)
upload_id = response['UploadId']
parts = []
try:
with open(file_path, 'rb') as f:
part_number = 1
while True:
data = f.read(part_size)
if not data:
break
part = s3.upload_part(
Bucket=bucket_name,
Key=key,
PartNumber=part_number,
UploadId=upload_id,
Body=data,
)
parts.append({'ETag': part['ETag'], 'PartNumber': part_number})
print(f'Uploaded part {part_number}')
part_number += 1
# 2. Complete upload
s3.complete_multipart_upload(
Bucket=bucket_name,
Key=key,
UploadId=upload_id,
MultipartUpload={'Parts': parts},
)
print('Multipart upload complete.')
except Exception as e:
# Abort upload
s3.abort_multipart_upload(Bucket=bucket_name, Key=key, UploadId=upload_id)
print('Multipart upload aborted due to error:', e)Uploaded part 1
Uploaded part 2
Uploaded part 3
Multipart upload complete.Common Issue Troubleshooting
| Issue | Cause | Solution |
|---|---|---|
SignatureDoesNotMatch | Not using v4 signature | Set signature_version='s3v4' |
EndpointConnectionError | Wrong RustFS address or service not started | Check endpoint and RustFS service status |
AccessDenied | Wrong credentials or insufficient permissions | Check AccessKey/SecretKey or bucket policies |
PermanentRedirect / wrong bucket URL | Path-style not enabled | Set s3={'addressing_style': 'path'} in the Config |
Appendix: Quick Upload/Download Script Template
def upload_file(local_path, bucket, object_key):
s3.upload_file(local_path, bucket, object_key)
print(f"Uploaded {local_path} to s3://{bucket}/{object_key}")
def download_file(bucket, object_key, local_path):
s3.download_file(bucket, object_key, local_path)
print(f"Downloaded s3://{bucket}/{object_key} to {local_path}")For other operations (object tagging, bucket policies, and more), see the Boto3 S3 documentation — every S3-compatible call works against RustFS the same way.