The PUT was always sent to 127.0.0.1, which is nothing inside the container, so uploads failed whenever the web player did not run on the same host as SeaweedFS. Take an optional S3_UPLOAD_ENDPOINT instead (the compose stacks point it at seaweedfs:8333) and fall back to the host in the presigned URL. The signed Host header is preserved either way, so the signature still matches.