Repository navigation
Description
Is there an existing issue for this?
- I have searched the existing issues
Current behavior
When setting USE_MINIO to 1 while using an external instance at another domain, omitting the route through the Plane web proxy, asset URLs are malformed. This stops them from being loaded.
This is due to assuming an external S3 endpoint would always be served behind the Plane web proxy. For decoupling the two services in a distributed system, it would be preferred being able to differentiate between these cases:
USE_MINIO/USE_INTERNAL_MINIOUSE_EXTERNAL_S3
It appears USE_MINIO actually means USE_INTERNAL_MINIO. It's naming is further complicated, since setting the variable is required to use other S3 providers as well.
plane/apiserver/plane/settings/common.py
Lines 230 to 233 in 9ee1d8c
| if AWS_S3_ENDPOINT_URL and USE_MINIO: | |
| parsed_url = urlparse(os.environ.get("WEB_URL", "http://localhost")) | |
| AWS_S3_CUSTOM_DOMAIN = f"{parsed_url.netloc}/{AWS_STORAGE_BUCKET_NAME}" | |
| AWS_S3_URL_PROTOCOL = f"{parsed_url.scheme}:" |
The common settings always assumes we deploy a non-AWS S3 behind the Plane web proxy
This works when AWS_S3_ENDPOINT_URL points to the internal http://plane-minio:9000. It fails when setting it to https://s3.example.com.
plane/apiserver/plane/app/views/project/base.py
Lines 603 to 609 in 9ee1d8c
| if settings.USE_MINIO: | |
| s3 = boto3.client( | |
| "s3", | |
| endpoint_url=settings.AWS_S3_ENDPOINT_URL, | |
| aws_access_key_id=settings.AWS_ACCESS_KEY_ID, | |
| aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY, | |
| ) |
The cover image endpoint only recognises the AWS_S3_ENDPOINT_URL when USE_MINIO is set.
This leaves out the case where we want to set a custom AWS_S3_ENDPOINT_URL, but do not want to serve the bucket behind the same load balancer, e.g. via another host. This is useful for independently scaling out application and backing services.
There is a PR which only partially resolves this situation, by allowing to configure the Minio URL in the web proxy:
In the cover image example below, we receive this URL:
While we would like to see this:
The URLs are generated in:
plane/apiserver/plane/bgtasks/export_task.py
Lines 92 to 99 in 9ee1d8c
| # Generate presigned url for the uploaded file with different base | |
| presign_s3 = boto3.client( | |
| "s3", | |
| endpoint_url=f"{settings.AWS_S3_URL_PROTOCOL}//{str(settings.AWS_S3_CUSTOM_DOMAIN).replace('/uploads', '')}/", | |
| aws_access_key_id=settings.AWS_ACCESS_KEY_ID, | |
| aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY, | |
| config=Config(signature_version="s3v4"), | |
| ) |
The wrong assumption in common.py about AWS_S3_CUSTOM_DOMAIN is, that we always want to serve Minio/other S3 providers that are not AWS behind the Plane load balancer. This assumption is also present in the proposed partial fix #3278.
There should be cases possible in which we do not replace the AWS_S3_CUSTOM_DOMAIN, even when USE_MINIO is set.
Related issues:
- [bug]: external s3/minio #1318
- [bug]: Minio uses localhost #2345
- Getting issue During S3 configuration #3490
- [bug]: Calls to AWS while using MinIO #4248
- [bug]: Can't get my uploaded images or attachements. #5680
Steps to reproduce
- Upload a cover image to a new project
- Save the project
- Find a 404 GET request in the browser console.
Environment
Production
Browser
Mozilla Firefox
Variant
Self-hosted
Version
v0.25.1
Metadata
Metadata
Assignees
Labels
Type
Fields
Edition
Version
Activity
almereyda commentedon Mar 10, 2025
A common workaround will be to hardcode one's own S3 host into
nginx.confor as an environmental variable into the template.To avoid an error with
upstream sent too big header while reading response header from upstream, it might also be necessary to increase the proxy buffer size innginx.conf:Adding a custom DNS resolver, here the one from Docker, helps to resolve external hostnames.
Unfortunately configuring a custom route in
nginx.confwill not produce correct pre-signed URLs from Plane, since they will not use the same host as the S3 service.Files can be uploaded, but yield a SignatureDoesNotMatch error:
These location blocks both worked for saving files to the external Minio.
Here Minio is configured with
MINIO_SERVER_URL=https://s3.example.comandMINIO_DOMAIN=s3.example.com.Loading files is not possible, due to a mismatch of the FQDN used for signing the URL.
Native support of external S3 hosts, such as Minio, without configuring it to use the same hostname as Plane, will be highly appreciate to circumvent this behaviour.
pablohashescobar commentedon Mar 11, 2025
@almereyda it should work with
USE_MINIO=0. Agreeing with you on changing the naming convention toUSE_INTERNAL_MINIOto indicate use of the attached minio instance.The setting file in
common.pyconfiguration shared above is old and will be removed in upcoming releases. The new storage configuration is present here.Setting
USE_MINIO=0should work please let me know if you are still facing the issue.gregoriusus commentedon Sep 8, 2025
I have same issue on 0,28.0 version. If I use USE_MINIO=0, I can upload, but then I get
SignatureDoesNotMatchThe request signature we calculated does not match the signature you provided. Check your key and signing method. .....
I am using selfhosted plane in docker
yairoman commentedon Sep 21, 2025
@pablohashescobar I am using the docker-compose of the last version:
https://github.com/makeplane/plane/releases/download/v1.0.0/docker-compose.yml
And I can not use an external minio, inclusive assgined the enviroment variables USE_MINIO=0, and AWS_...
rtgiskard commentedon Dec 14, 2025
I'm using seaweedfs as external s3 storage, files get upload to
bucket_name$uuidrather thanbucket_name/$uuid, while with minio, I do not need to change anything (change the httproute to switch s3 to minio), post/fetch works well, but how to make it work with seaweedfs s3?rtgiskard commentedon Dec 23, 2025
seaweedfs bug resolved in 4.03: seaweedfs/seaweedfs#7714