Cloudflare R2 Image Backup
This repo uses Cloudflare R2 (S3-compatible object storage) as a free backup/CDN for images. Local markdown files keep using relative paths -- R2 is just a mirror.
Account Details
- Bucket:
kubequest-images - Public URL:
https://pub-03b68c7e797949c2add7d7d298478f58.r2.dev/ - S3 Endpoint:
https://ac1c3693224388b2d23c4e01733339ea.r2.cloudflarestorage.com - Free tier: 10 GB storage, 1M Class A ops, 10M Class B ops/month, zero egress fees
Setup (One-Time)
1. Install rclone
bash
# Arch Linux
sudo pacman -S rclone
# Ubuntu/Debian
sudo apt install rclone
# Or universal install
curl https://rclone.org/install.sh | sudo bash2. Configure rclone
Credentials are stored in .env (gitignored). Load them and create the rclone config:
bash
source .env
mkdir -p ~/.config/rclone
cat > ~/.config/rclone/rclone.conf << EOF
[r2]
type = s3
provider = Cloudflare
access_key_id = ${R2_ACCESS_KEY_ID}
secret_access_key = ${R2_SECRET_ACCESS_KEY}
endpoint = ${R2_ENDPOINT}
acl = private
no_check_bucket = true
EOFOr manually edit ~/.config/rclone/rclone.conf and paste:
ini
[r2]
type = s3
provider = Cloudflare
access_key_id = <from .env>
secret_access_key = <from .env>
endpoint = https://ac1c3693224388b2d23c4e01733339ea.r2.cloudflarestorage.com
acl = private
no_check_bucket = true3. Verify connection
bash
rclone lsd r2:kubequest-imagesUploading Images
Upload all images in the repo (full sync)
bash
cd /home/vagabond/Development/kubequest
rclone copy . r2:kubequest-images \
--include "*.png" \
--include "*.jpg" \
--include "*.jpeg" \
--include "*.gif" \
--include "*.webp" \
--exclude ".git/**" \
--exclude "node_modules/**" \
--transfers 16 \
--checkers 16 \
-vThis skips already-uploaded files automatically (rclone checks by size/hash).
Upload a specific directory
bash
# Upload just the CCNA screenshots
rclone copy networking/ccna/cbt-nugget/ r2:kubequest-images/networking/ccna/cbt-nugget/ \
--include "*.png" \
--transfers 16 \
-v
# Upload just the Azure assets
rclone copy azure/ r2:kubequest-images/azure/ \
--include "*.png" \
--transfers 16 \
-vUpload a single file
bash
rclone copyto path/to/image.png r2:kubequest-images/path/to/image.pngAccessing Images
Any uploaded image is publicly accessible at:
https://pub-03b68c7e797949c2add7d7d298478f58.r2.dev/<path-in-bucket>Example:
Local: azure/az-104/01-identity-governance/scott-duffy/assets/entra-pricing-banner.png
URL: https://pub-03b68c7e797949c2add7d7d298478f58.r2.dev/azure/az-104/01-identity-governance/scott-duffy/assets/entra-pricing-banner.pngThe path in R2 mirrors the repo structure exactly.
Using R2 URLs in Markdown
If you want to reference an R2-hosted image instead of a local one:
markdown
<!-- Local (default, keeps working offline) -->

<!-- R2 (for large images you don't want in git) -->
Useful Commands
bash
# Check what's in the bucket
rclone ls r2:kubequest-images | head -20
# Check bucket size
rclone size r2:kubequest-images
# Dry run (see what would upload without uploading)
rclone copy . r2:kubequest-images \
--include "*.png" \
--exclude ".git/**" \
--exclude "node_modules/**" \
--dry-run
# Delete a file from R2
rclone deletefile r2:kubequest-images/path/to/image.png
# Sync (upload new + delete removed -- be careful)
rclone sync . r2:kubequest-images \
--include "*.png" \
--exclude ".git/**" \
--exclude "node_modules/**" \
--dry-run # remove --dry-run when readyCredentials
Stored in .env (gitignored):
| Variable | Description |
|---|---|
CLOUDFLARE_API_TOKEN | Cloudflare API bearer token |
CLOUDFLARE_ACCOUNT_ID | Cloudflare account ID |
R2_ACCESS_KEY_ID | S3-compatible access key |
R2_SECRET_ACCESS_KEY | S3-compatible secret key |
R2_ENDPOINT | S3 endpoint URL |
R2_BUCKET | Bucket name (kubequest-images) |
To regenerate S3 credentials: Cloudflare Dashboard > R2 > Manage API Tokens > Create API Token.