A simple S3-compatible API server that uses WebDAV as the underlying storage backend.
This project is BETA, and might contain bugs, use it with caution.
This server is NOT intended for internet exposure. It cannot handle such load. It implements AWS v2 signature authentication and should be used on trusted networks or with HTTPS enabled. The intent usage is to run it locally to PBS, and use it only with PBS.
If you found it useful :)
The server connects to the WebDAV server, scans specified bucket directories into a SQLite database for fast lookups, and provides an S3-compatible HTTP API. When you upload/download files through the S3 API, they are stored on/retrieved from the WebDAV server. The database cache is kept in sync automatically.
The initial sync for buckets might take significant amount of time. No data will be served once the buckets are scanned. The database might become out of sync if files are manually created on bucket, in such case the metadata.db has to be removed.
This server is designed for use with Proxmox Backup Server and connecting it to Hetzner Storage Box WebDAV, and supports limited amount of features to make it work with PBS.
Bucket Filtering: You must specify which WebDAV directories to expose as S3 buckets. Only the specified buckets will be synced and accessible via the S3 API.
Configure via environment variables or command-line flags:
WEBDAV_URL="https://your-webdav-server.com/dav"
WEBDAV_USER="your-username"
WEBDAV_PASSWORD="your-password"
BUCKETS="bucket1,bucket2,bucket3" # Comma-separated list of bucket names to syncHTTP_PORT="8080" # HTTPS server port
WEBDAV_INSECURE="false" # Allow self-signed WebDAV certificates
AWS_ACCESS_KEY_ID="key" # S3 access key (optional - auto-generated if not provided)
AWS_SECRET_ACCESS_KEY="secret" # S3 secret key (optional - auto-generated if not provided)
AWS_ACCESS_INSECURE="true" # Allow insecure access without authentication
TLS_CERT="cert.pem" # Custom TLS certificate
TLS_KEY="key.pem" # Custom TLS private key
PERSIST_DIR="./data" # Directory for persistent data (certificates and S3 keys)
READ_ONLY="true" # Enable read-only mode (disables PUT, DELETE operations)- Secure Mode (default): S3 keys are auto-generated and stored in
PERSIST_DIR, or use providedAWS_ACCESS_KEY_IDandAWS_SECRET_ACCESS_KEY. Requests must include proper AWS signature authentication (supports both v2 and v4 signatures). - Insecure Mode: Set
AWS_ACCESS_INSECURE=trueto disable authentication entirely (not recommended).
Signature Support: The server supports both AWS Signature Version 2 and Version 4 authentication:
- AWS v2: Traditional
Authorization: AWS AccessKey:Signatureheaders and presigned URLs - AWS v4: Modern
Authorization: AWS4-HMAC-SHA256 ...headers and presigned URLs withX-Amz-*parameters
- Auto-generated: Use
PERSIST_DIRfor self-signed certificates (10-year validity) (default) - Custom certificates: Use
TLS_CERTandTLS_KEY - HTTP: Run without TLS. Use
HTTP_ONLY
./s3-to-webdav -webdav-url "https://your-server.com/dav" \
-webdav-user "user" \
-webdav-password "pass" \
-buckets "bucket1,bucket2,bucket3"# With auto-generated credentials (check server logs for keys)
aws configure set aws_access_key_id <generated-access-key>
aws configure set aws_secret_access_key <generated-secret-key>
aws --endpoint-url http://localhost:8080 s3 ls
# With provided credentials
aws configure set aws_access_key_id your-access-key
aws configure set aws_secret_access_key your-secret-key
aws --endpoint-url http://localhost:8080 s3 ls
# Insecure mode (authentication disabled)
AWS_ACCESS_INSECURE=true ./s3-to-webdav [other-flags]
aws configure set aws_access_key_id dummy
aws configure set aws_secret_access_key dummy
aws --endpoint-url http://localhost:8080 s3 lsdocker run -v /path/to/data:/data \
-e WEBDAV_URL="https://your-server.com/dav" \
-e WEBDAV_USER="user" \
-e WEBDAV_PASSWORD="pass" \
-e BUCKETS="bucket1,bucket2,bucket3" \
-p 8080:8080 s3-to-webdavUse the included docker-compose.yml file:
# Edit docker-compose.yml with your WebDAV credentials and bucket list
docker-compose up -d
# View logs to see generated credentials
docker-compose logs s3-to-webdavWhen the server starts, it displays all important information in the logs:
S3: Generated/loaded credentials from ./data
S3: Access Key: a1b2c3d4e5f67890
S3: Secret Key: 1a2b3c4d5e6f7890abcdef1234567890abcdef12
TLS: Certificate: ./data/cert.pem / ./data/key.pem
TLS: Fingerprint: SHA256:1A:2B:3C:4D:5E:6F:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90
HTTPS: Server ready! Listening on https://:8080
docker logs <container-name>
docker-compose logs s3-to-webdavThis uses the Proxmox Backup Server in a Container.
Docker Compose:
services:
pbs:
image: ayufan/proxmox-ve:latest
ports:
- 8007:8007
mem_limit: 2G
volumes:
- ./pbs-etc:/etc/proxmox-backup
- ./pbs-log:/var/log/proxmox-backup
- ./pbs-lib:/var/lib/proxmox-backup
- ./pbs-backups:/backups
tmpfs:
- /run
restart: unless-stopped
stop_signal: SIGHUP
hetzner-s3:
build: https://github.com/ayufan-research/s3-to-webdav.git
restart: unless-stopped
volumes:
- ./hetzner-s3:/data
environment:
WEBDAV_URL: "https://your-username.your-server.de"
WEBDAV_USER: "your-username"
WEBDAV_PASSWORD: "your-password"
BUCKETS: "pbs-backups"In PBS web interface: Configuration → S3 Endpoints → Add
- S3 Endpoint ID:
hetzner-s3 - Port:
8080 - Region: not relevant
- Access Key ID: [from container logs]
- Secret Access Key: [from container logs]
- Fingerprint: [from container logs]
⚠️ Path Style: ✅ Must be enabled
Then add datastore: : Datastore → Add Datastore.
docker-compose logs hetzner-s3Good for personal use.
Kamil Trzciński, 2025, with the help of Claude Code.