Practical Guide

Deploying a Static Site
to Quilibrium QStorage

A practical guide using Python / boto3

1 / 10
Prerequisites

What You'll Need

🔐

Quilibrium Account

Active account with QStorage enabled in QConsole

🔑

API Credentials

Access Key ID + Secret Access Key generated from QConsole

🐍

Python 3 + boto3

Python 3.8+ with the boto3 library installed
pip install boto3

📁

Static Site Files

Your HTML, CSS, JS, and assets ready to upload

2 / 10
⚠️

Gotcha: AWS CLI v2 Doesn't Work

The Problem

AWS CLI v2 uses aws-chunked transfer encoding by default

It sends CRC64NVME checksums that QStorage rejects

Result: uploads fail silently or with cryptic errors

The Fix

Use Python boto3 — it sends standard requests without chunked encoding

boto3 is the officially recommended approach for QStorage

If you must use CLI, try aws-cli v1 with --no-verify-ssl

3 / 10
Configuration

Get Credentials & Configure

How to get credentials

01

Log in to QConsole

02

Navigate to Services → QStorage

03

Click Generate API Keys

04

Copy Access Key ID + Secret Access Key

💡 Note: API key approval may require contacting support channels. Check your QConsole dashboard for status.

📄 ~/.aws/credentials
[qstorage] aws_access_key_id = YOUR_ACCESS_KEY aws_secret_access_key = YOUR_SECRET_KEY
📄 ~/.aws/config
[profile qstorage] region = us-east-1 endpoint_url = https://storage.quilibrium.com
4 / 10
Code

Create Bucket & Upload Files

🐍 create_bucket.py
import boto3 # Connect to QStorage s3 = boto3.client( 's3', endpoint_url='https://storage.quilibrium.com', aws_access_key_id='YOUR_KEY', aws_secret_access_key='YOUR_SECRET', region_name='us-east-1' ) # Create a bucket s3.create_bucket(Bucket='my-static-site') print("✓ Bucket created")
🐍 upload_files.py
import os # Upload with correct Content-Type def upload_directory(s3, bucket, local_dir): for root, dirs, files in os.walk(local_dir): for f in files: path = os.path.join(root, f) key = os.path.relpath(path, local_dir) s3.upload_file( path, bucket, key, ExtraArgs={'ContentType': guess_type(f)} ) print(f"↑ {key}")
5 / 10
🔑

CRITICAL: Apply Public-Read ACL Separately

❌ Wrong — ACL is silently stripped

s3.upload_file( 'index.html', 'my-site', 'index.html', ExtraArgs={ 'ContentType': 'text/html', 'ACL': 'public-read' # ← IGNORED! } )

QStorage silently ignores the ACL key inside ExtraArgs during upload.

✅ Correct — Separate ACL call

# 1. Upload the file s3.upload_file( 'index.html', 'my-site', 'index.html', ExtraArgs={'ContentType': 'text/html'} ) # 2. Apply ACL in a separate call s3.put_object_acl( Bucket='my-site', Key='index.html', ACL='public-read' )
6 / 10
Configuration

Website Hosting & Bucket Policy

🐍 enable_website.py
# Enable static website hosting s3.put_bucket_website( Bucket='my-static-site', WebsiteConfiguration={ 'IndexDocument': { 'Suffix': 'index.html' }, 'ErrorDocument': { 'Key': '404.html' } } ) print("✓ Website hosting enabled")
🐍 set_policy.py
import json policy = { "Version": "2012-10-17", "Statement": [{ "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "*", "Action": ["s3:GetObject"], "Resource": [ "arn:aws:s3:::my-static-site/*" ] }] } s3.put_bucket_policy( Bucket='my-static-site', Policy=json.dumps(policy) ) print("✓ Policy applied")
7 / 10
Access

Access Your Site

❌ Path-Style URL
# Requires authentication https://storage.quilibrium.com/ my-static-site/index.html

Path-style URLs require authentication headers. Browsers can't send these — visitors will get Access Denied.

✅ Virtual-Hosted Style
# Works publicly https://my-static-site .s3.quilibrium-storage.com

Virtual-hosted–style URLs route through the website endpoint. The bucket policy + ACL grant public access — works in any browser.

💡 Tip: Add a custom domain via DNS CNAME pointing to your virtual-hosted URL for a branded experience.

8 / 10
Debugging

Troubleshooting

Symptom Cause Fix
Access Denied on website URL Missing bucket policy or ACL Apply put_bucket_policy + put_object_acl per object
Upload fails with encoding error AWS CLI v2 chunked encoding Switch to boto3; avoid AWS CLI v2 with QStorage
404 on index.html Website hosting not enabled Call put_bucket_website with correct index suffix
CSS/JS not loading Wrong Content-Type on upload Set ContentType in ExtraArgs: text/css, application/javascript
Credentials not found Wrong profile or missing config Verify ~/.aws/credentials and ~/.aws/config paths
9 / 10
Quick Reference

Minimal deploy.py in 6 Steps

1

Create S3 client with QStorage endpoint

2

create_bucket()

3

Upload files with ContentType

4

put_object_acl() per file

5

put_bucket_website()

6

put_bucket_policy()

🐍 deploy.py — outline
import boto3, json, os from mimetypes import guess_type s3 = boto3.client('s3', ...) s3.create_bucket(Bucket='my-site') for f in walk_files('./dist'): s3.upload_file(f, 'my-site', key, ExtraArgs={'ContentType': ...}) s3.put_object_acl( Bucket='my-site', Key=key, ACL='public-read') s3.put_bucket_website(...) s3.put_bucket_policy(...) print("🚀 Deployed!")
10 / 10