fix AWS S3 startup for production deployments
Docker server image / build-and-push (push) Successful in 1m7s

Pass S3_REGION and S3_PUBLIC_USE_SSL through compose, treat blank public
SSL as unset, and skip CreateBucket when IAM only allows access to an
existing bucket.
This commit is contained in:
2026-06-17 11:55:53 -07:00
parent 1756a4a0f8
commit 26cd0ef071
4 changed files with 13 additions and 6 deletions
+2
View File
@@ -24,7 +24,9 @@ services:
S3_ENDPOINT: ${S3_ENDPOINT:-seaweedfs:8333} S3_ENDPOINT: ${S3_ENDPOINT:-seaweedfs:8333}
S3_PUBLIC_ENDPOINT: ${S3_PUBLIC_ENDPOINT:-localhost:8333} S3_PUBLIC_ENDPOINT: ${S3_PUBLIC_ENDPOINT:-localhost:8333}
S3_BUCKET: ${S3_BUCKET:-officeconvert} S3_BUCKET: ${S3_BUCKET:-officeconvert}
S3_REGION: ${S3_REGION:-}
S3_USE_SSL: ${S3_USE_SSL:-false} S3_USE_SSL: ${S3_USE_SSL:-false}
S3_PUBLIC_USE_SSL: ${S3_PUBLIC_USE_SSL:-}
S3_ACCESS_KEY: ${S3_ACCESS_KEY:-minioadmin} S3_ACCESS_KEY: ${S3_ACCESS_KEY:-minioadmin}
S3_SECRET_KEY: ${S3_SECRET_KEY:-minioadmin} S3_SECRET_KEY: ${S3_SECRET_KEY:-minioadmin}
S3_SESSION_TTL_SECONDS: ${S3_SESSION_TTL_SECONDS:-3600} S3_SESSION_TTL_SECONDS: ${S3_SESSION_TTL_SECONDS:-3600}
+2
View File
@@ -23,7 +23,9 @@ services:
S3_ENDPOINT: ${S3_ENDPOINT:-seaweedfs:8333} S3_ENDPOINT: ${S3_ENDPOINT:-seaweedfs:8333}
S3_PUBLIC_ENDPOINT: ${S3_PUBLIC_ENDPOINT:-localhost:8333} S3_PUBLIC_ENDPOINT: ${S3_PUBLIC_ENDPOINT:-localhost:8333}
S3_BUCKET: ${S3_BUCKET:-officeconvert} S3_BUCKET: ${S3_BUCKET:-officeconvert}
S3_REGION: ${S3_REGION:-}
S3_USE_SSL: ${S3_USE_SSL:-false} S3_USE_SSL: ${S3_USE_SSL:-false}
S3_PUBLIC_USE_SSL: ${S3_PUBLIC_USE_SSL:-}
S3_ACCESS_KEY: ${S3_ACCESS_KEY:-minioadmin} S3_ACCESS_KEY: ${S3_ACCESS_KEY:-minioadmin}
S3_SECRET_KEY: ${S3_SECRET_KEY:-minioadmin} S3_SECRET_KEY: ${S3_SECRET_KEY:-minioadmin}
S3_SESSION_TTL_SECONDS: ${S3_SESSION_TTL_SECONDS:-3600} S3_SESSION_TTL_SECONDS: ${S3_SESSION_TTL_SECONDS:-3600}
@@ -31,10 +31,10 @@ class ServerConfig:
def load_server_config() -> ServerConfig: def load_server_config() -> ServerConfig:
"""Load server configuration from environment variables.""" """Load server configuration from environment variables."""
s3_secure = os.getenv("S3_USE_SSL", "false").lower() == "true" s3_secure = os.getenv("S3_USE_SSL", "false").lower() == "true"
public_ssl_env = os.getenv("S3_PUBLIC_USE_SSL") public_ssl_env = os.getenv("S3_PUBLIC_USE_SSL", "").strip()
s3_public_secure = ( s3_public_secure = (
public_ssl_env.lower() == "true" public_ssl_env.lower() == "true"
if public_ssl_env is not None if public_ssl_env
else s3_secure else s3_secure
) )
region_env = os.getenv("S3_REGION", "").strip() region_env = os.getenv("S3_REGION", "").strip()
@@ -86,16 +86,19 @@ class S3Store:
def ensure_bucket(self, bucket_name: str) -> None: def ensure_bucket(self, bucket_name: str) -> None:
"""Create a bucket if it does not already exist. """Create a bucket if it does not already exist.
Uses CreateBucket only, not HeadBucket. Some S3-compatible stores Tries CreateBucket first (idempotent on SeaweedFS and when the caller
(including SeaweedFS) mishandle or over-restrict HeadBucket; the MinIO owns the bucket). AWS production IAM often grants object access only on
client's bucket_exists() maps non-NoSuchBucket errors to failures. a pre-provisioned bucket; in that case CreateBucket returns
Idempotent create covers the same contract with fewer round trips. AccessDenied even though HeadBucket succeeds.
""" """
try: try:
self._client.make_bucket(bucket_name) self._client.make_bucket(bucket_name)
except S3Error as exc: except S3Error as exc:
if exc.code in ("BucketAlreadyOwnedByYou", "BucketAlreadyExists"): if exc.code in ("BucketAlreadyOwnedByYou", "BucketAlreadyExists"):
return return
if exc.code in ("AccessDenied", "Forbidden"):
if self._client.bucket_exists(bucket_name):
return
raise raise
def presigned_put_url(self, bucket_name: str, object_key: str, *, ttl_seconds: int) -> str: def presigned_put_url(self, bucket_name: str, object_key: str, *, ttl_seconds: int) -> str: