Skip to content

Notifuse/selfhost_s3

Repository files navigation

selfhost_s3

Go Docker Docker Hub codecov Go Report Card

A minimal S3-compatible object storage server written in Go that persists files to the local filesystem. Designed for development and self-hosted deployments where a full S3 service is overkill.

Features

  • S3-compatible API (AWS Signature V4 authentication)
  • Local filesystem storage
  • Public file access (optional prefix-based)
  • Download mode with ?download=1 query parameter
  • Configurable cache headers for public files
  • Single binary, no dependencies
  • Multi-platform Docker images (amd64, arm64)

Supported S3 Operations

selfhost_s3 implements the minimum S3 API required by the Notifuse file manager:

Operation Description
GetObject Download/serve files (used for file URLs)
ListObjectsV2 List all objects in the bucket
PutObject Upload files and create folders
DeleteObject Delete files and folders
HeadObject Check if file exists (optional, but recommended)

Quick Start

Docker (Recommended)

Pull and run directly from Docker Hub:

docker run -d \
  --name selfhost_s3 \
  -p 9000:9000 \
  -v $(pwd)/data:/data \
  -e S3_BUCKET=my-bucket \
  -e S3_ACCESS_KEY=myaccesskey \
  -e S3_SECRET_KEY=mysecretkey \
  notifuse/selfhost_s3:latest

Docker Compose

Create a compose.yaml:

services:
  selfhost_s3:
    image: notifuse/selfhost_s3:latest
    container_name: selfhost_s3
    restart: unless-stopped
    ports:
      - "9000:9000"
    volumes:
      - ./s3-data:/data
    environment:
      S3_BUCKET: my-bucket
      S3_ACCESS_KEY: myaccesskey
      S3_SECRET_KEY: mysecretkey

Then run:

docker compose up -d

Build from Source

# Clone the repository
git clone https://github.com/Notifuse/selfhost_s3.git
cd selfhost_s3

# Build
go build -o selfhost_s3 ./cmd/selfhost_s3

# Run
export S3_BUCKET=my-bucket
export S3_ACCESS_KEY=myaccesskey
export S3_SECRET_KEY=mysecretkey
./selfhost_s3

Configuration

selfhost_s3 is configured via environment variables:

Variable Required Default Description
S3_BUCKET Yes - S3 bucket name
S3_ACCESS_KEY Yes - Access key for authentication
S3_SECRET_KEY Yes - Secret key for authentication
S3_PORT No 9000 Port to listen on
S3_STORAGE_PATH No ./data Local directory for file storage
S3_REGION No us-east-1 AWS region (for signature validation)
S3_CORS_ORIGINS No * Allowed CORS origins (comma-separated)
S3_MAX_FILE_SIZE No 100MB Maximum upload file size
S3_PUBLIC_PREFIX No public/ Prefix for public files (empty string disables)
S3_PUBLIC_CACHE_MAX_AGE No 31536000 Cache-Control max-age for public files (seconds)

Docker Hub

Official images are available at hub.docker.com/r/notifuse/selfhost_s3

Available tags:

  • latest - Latest stable release
  • vX.Y - Specific version (e.g., v1.0)

Supported platforms:

  • linux/amd64
  • linux/arm64

Connecting from Notifuse

In your workspace settings, configure the File Manager with:

  • Endpoint: http://localhost:9000 (or your selfhost_s3 URL)
  • Bucket: Your S3_BUCKET value
  • Access Key: Your S3_ACCESS_KEY value
  • Secret Key: Your S3_SECRET_KEY value
  • Region: us-east-1 (default)
  • Path-style: Enabled (required)

Storage Structure

Files are stored on the local filesystem mirroring the S3 key structure:

data/
└── my-bucket/
    ├── documents/
    │   └── report.pdf
    └── images/
        └── logo.png
  • Files: Stored at {storage_path}/{bucket}/{key}
  • Folders: Represented as empty files with keys ending in /

Public Access

selfhost_s3 supports serving files publicly without authentication. By default, files under the public/ prefix are accessible via GET and HEAD requests without AWS Signature V4 authentication.

How It Works

  • The public/ directory is automatically created on server startup
  • GET and HEAD requests to paths starting with the public prefix skip authentication
  • PUT and DELETE operations still require authentication (only uploads/deletes are protected)
  • Public files include Cache-Control headers for CDN/browser caching

Public File URLs

Files in the public prefix can be accessed directly via browser:

http://localhost:9000/my-bucket/public/images/logo.png
http://localhost:9000/my-bucket/public/documents/report.pdf

Download Mode

Add ?download=1 to force the browser to download the file instead of displaying it:

http://localhost:9000/my-bucket/public/report.pdf?download=1

This sets the Content-Disposition: attachment header with the filename.

Configuration Examples

Custom public prefix:

S3_PUBLIC_PREFIX=assets/  # Files under assets/ are public

Disable public access entirely:

S3_PUBLIC_PREFIX=  # Empty string disables public access

Custom cache duration (1 hour):

S3_PUBLIC_CACHE_MAX_AGE=3600

Disable caching:

S3_PUBLIC_CACHE_MAX_AGE=0

Security Recommendations

When exposing files publicly:

  1. Use a reverse proxy (nginx, Caddy, Traefik) in front of selfhost_s3 for SSL/TLS
  2. Restrict CORS origins in production: S3_CORS_ORIGINS=https://yourdomain.com
  3. Consider a CDN for frequently accessed public files
  4. Validate uploads in your application before storing files in the public prefix

CORS

Since browser clients connect directly to selfhost_s3, CORS headers are automatically included:

Access-Control-Allow-Origin: <from S3_CORS_ORIGINS>
Access-Control-Allow-Methods: GET, HEAD, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization, x-amz-*
Access-Control-Expose-Headers: ETag, Content-Length, Content-Type

For production, set S3_CORS_ORIGINS to your specific domain(s):

S3_CORS_ORIGINS=https://app.example.com,https://admin.example.com

Health Check

selfhost_s3 exposes a health endpoint for container orchestration:

GET /health

Returns 200 OK with {"status": "ok"} when the server is running.

API Examples

Get File (Download/View)

# Via AWS CLI
aws s3 cp s3://my-bucket/path/myfile.txt ./myfile.txt \
  --endpoint-url http://localhost:9000

# Direct URL (for browser/images)
curl http://localhost:9000/my-bucket/path/image.png

List Objects

aws s3api list-objects-v2 \
  --endpoint-url http://localhost:9000 \
  --bucket my-bucket

Upload File

aws s3 cp myfile.txt s3://my-bucket/path/myfile.txt \
  --endpoint-url http://localhost:9000

Delete File

aws s3 rm s3://my-bucket/path/myfile.txt \
  --endpoint-url http://localhost:9000

Limitations

  • No multipart uploads: Files are uploaded in a single request
  • No presigned URLs: Direct authentication required
  • No versioning: Files are overwritten in place
  • No bucket operations: Bucket must be pre-configured via env var
  • Single bucket: One selfhost_s3 instance = one bucket

Development

# Run tests
go test ./...

# Run with hot reload
go run ./cmd/selfhost_s3

Implementation Notes

  • Standard library only - net/http is sufficient, no web framework needed
  • AWS Signature V4 - Validates signatures with proper URI encoding for special characters
  • File locking - Uses sync.RWMutex for concurrent read/write safety
  • Content-Type - Guessed from file extension using Go's mime package
  • ETag - Generated from file modification time and size
  • Path traversal - Keys are sanitized to prevent ../ attacks

License

MIT

About

A minimal S3-compatible object storage server that persists files to the local filesystem.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •  

Languages