Object storage in Windmill (S3)
Instance and workspace object storage are different from using S3 resources within scripts, flows, and apps, which is free and unlimited.
At the workspace level, what is exclusive to the Enterprise version is using the integration of Windmill with S3 that is a major convenience layer to enable users to read and write from S3 without having to have access to the credentials.
Additionally, for instance integration, the Enterprise version offers advanced features such as large-scale log management and distributed dependency caching.
Workspace object storage
Connect your Windmill workspace to your S3 bucket or your Azure Blob storage to enable users to read and write from S3 without having to have access to the credentials.
Windmill S3 bucket browser will not work for buckets containing more than 20 files and uploads are limited to files < 50MB. Consider upgrading to Windmill Enterprise Edition to use this feature with large buckets.
Once you've created an S3 or Azure Blob resource in Windmill, go to the workspace settings > S3 Storage. Select the resource and click Save.
From now on, Windmill will be connected to this bucket and you'll have easy access to it from the code editor and the job run details. If a script takes as input a s3object
, you will see in the input form on the right a button helping you choose the file directly from the bucket.
Same for the result of the script. If you return an s3object
containing a key s3
pointing to a file inside your bucket, in the result panel there will be a button to open the bucket explorer to visualize the file.
S3 files in Windmill are just pointers to the S3 object using its key. As such, they are represented by a simple JSON:
{
"s3": "path/to/file"
}
Resources permissions
The resource can be set to be public with toggle "S3 resource details can be accessed by all users of this workspace".
In this case, the permissions set on the resource will be ignored when users interact with the S3 bucket via Windmill. Note that when the resource is public, the users might be able to access all of its details (including access keys and secrets) via some Windmill endpoints.
When the resource is not set to be public, Windmill guarantees that users who don't have access to the resource won't be able to retrieve any of its details. That being said, access to a specific file inside the bucket will still be possible, and downloading and uploading objects will also be accessible to any workspace user. In short, as long as the user knows the path of the file they want to access, they will be able to read its content. The main difference is that users won't be able to browse the content of the bucket.
When a script accepts a S3 file as input, it can be directly uploaded or chosen from the bucket explorer.
When a script outputs a S3 file, it can be downloaded or previewed directly in Windmill's UI (for displayable files like text files, CSVs or parquet files).
Even though the whole file is downloadable, the backend only sends the rows that the frontend needs for the preview. This means that you can manipulate objects of infinite size, and the backend will only return what is necessary.
You can even display several S3 files through an array of S3 objects:
export async function main() {
return [{s3: "path/to/file_1"}, {s3: "path/to/file_2", {s3: "path/to/file_3"}}];
}
Read a file from S3 or object storage within a script
S3Object
is a type that represents a file in S3 or object storage.
S3 files in Windmill are just pointers to the S3 object using its key. As such, they are represented by a simple JSON:
{
"s3": "path/to/file"
}
You can read a file from S3 or object storage within a script using the loadS3File
and loadS3FileStream
functions from the TypeScript client and the wmill.load_s3_file
and wmill.load_s3_file_stream
functions from the Python client.
- TypeScript (Bun)
- TypeScript (Deno)
- Python
import * as wmill from 'windmill-client';
import { S3Object } from 'windmill-client';
export async function main() {
const example_file: S3Object = {
s3: 'path/to/file'
};
// Load the entire file_content as a Uint8Array
const file_content = await wmill.loadS3File(example_file);
const decoder = new TextDecoder();
const file_content_str = decoder.decode(file_content);
console.log(file_content_str);
// Or load the file lazily as a Blob
let fileContentBlob = await wmill.loadS3FileStream(example_file);
console.log(await fileContentBlob.text());
}
import * as wmill from 'npm:[email protected]';
import { S3Object } from 'npm:[email protected]';
export async function main() {
const example_file: S3Object = {
s3: 'path/to/file'
};
// Load the entire file_content as a Uint8Array
const file_content = await wmill.loadS3File(example_file);
const decoder = new TextDecoder();
const file_content_str = decoder.decode(file_content);
console.log(file_content_str);
// Or load the file lazily as a Blob
let fileContentBlob = await wmill.loadS3FileStream(example_file);
console.log(await fileContentBlob.text());
}
import wmill
from wmill import S3Object
def main():
example_file = S3Object(s3='path/to/file')
# Load the entire file_content as a bytes array
file_content = wmill.load_s3_file(example_file)
print(file_content.decode('utf-8'))
# Or load the file lazily as a Buffered reader:
with wmill.load_s3_file_reader(example_file) as file_reader:
print(file_reader.read())
Certain file types, typically parquet files, can be directly rendered by Windmill.
Take a file as input
Scripts can accept a S3Object as input.
- TypeScript (Bun)
- TypeScript (Deno)
- Python
import * as wmill from 'windmill-client';
import { S3Object } from 'windmill-client';
export async function main(input_file: S3Object) {
// rest of the code
}
import * as wmill from 'npm:[email protected]';
import { S3Object } from 'npm:[email protected]';
export async function main(input_file: S3Object) {
// rest of the code
}
import wmill
from wmill import S3Object
def main(input_file: S3Object):
# Rest of the code
The auto-generated UI will display a file uploader:
or you can fill path manually if you enable 'Raw S3 object input':
and access bucket explorer if resource permissions allow it:
That's also the recommended way to pass S3 files as input to steps within flows.
Create a file from S3 or object storage within a script
You can create a file from S3 or object storage within a script using the writeS3File
function from the TypeScript client and the wmill.write_s3_file
function from the Python client.
- TypeScript (Bun)
- TypeScript (Deno)
- Python
import * as wmill from 'windmill-client';
import { S3Object } from 'windmill-client';
export async function main(s3_file_path: string) {
const s3_file_output: S3Object = {
s3: s3_file_path
};
const file_content = 'Hello Windmill!';
// file_content can be either a string or ReadableStream<Uint8Array>
await wmill.writeS3File(s3_file_output, file_content);
return s3_file_output;
}
import * as wmill from 'npm:[email protected]';
import { S3Object } from 'npm:[email protected]';
export async function main(s3_file_path: string) {
const s3_file_output: S3Object = {
s3: s3_file_path
};
const file_content = 'Hello Windmill!';
// file_content can be either a string or ReadableStream<Uint8Array>
await wmill.writeS3File(s3_file_output, file_content);
return s3_file_output;
}
import wmill
from wmill import S3Object
def main(s3_file_path: str):
s3_file_output = S3Object(s3=s3_file_path)
file_content = b"Hello Windmill!"
# file_content can be either bytes or a BufferedReader
file_content = wmill.write_s3_file(s3_file_output, file_content)
return s3_file_output
For more info on how to use files and S3 files in Windmill, see Handling files and binary data.