diff --git a/changelog/2025-08-12-ruby-support/index.md b/changelog/2025-08-12-ruby-support/index.md
new file mode 100644
index 000000000..d66f7e6e0
--- /dev/null
+++ b/changelog/2025-08-12-ruby-support/index.md
@@ -0,0 +1,17 @@
+---
+slug: ruby-support
+version: v1.XXX.0
+title: Ruby support
+tags: ['Ruby', 'Script editor']
+video: ./videos/ruby.mp4
+description: Windmill now supports Ruby scripts with bundler-compatible dependency management.
+features:
+ [
+ 'Write your Windmill script in Ruby.',
+ 'Flexible and simple syntax.',
+ 'Fast dependency installer with caching.',
+ 'Bundler-compatible gemfile syntax for dependency management.',
+ 'Private gem repository support.',
+ ]
+docs: /docs/getting_started/scripts_quickstart/ruby
+---
diff --git a/docs/core_concepts/9_worker_groups/index.mdx b/docs/core_concepts/9_worker_groups/index.mdx
index 4483043ac..8eae8f237 100644
--- a/docs/core_concepts/9_worker_groups/index.mdx
+++ b/docs/core_concepts/9_worker_groups/index.mdx
@@ -131,6 +131,7 @@ The tags of _default_ worker group are:
- `csharp`: The default worker group for [C#](../../getting_started/0_scripts_quickstart/11_csharp_quickstart/index.mdx) scripts.
- `java`: The default worker group for [Java](../../getting_started/0_scripts_quickstart/13_java_quickstart/index.mdx) scripts.
- `nu`: The default worker group for [Nu scripts](../../getting_started/0_scripts_quickstart/4_bash_quickstart/index.mdx#nu).
+- `ruby`: The default worker group for [Ruby](../../getting_started/0_scripts_quickstart/ruby_quickstart/index.mdx) scripts.
- `other`: Everything else (other than the [native](#native-worker-group) tags).
#### Native worker group
diff --git a/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/customize-ui.png b/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/customize-ui.png
new file mode 100644
index 000000000..341377a1a
Binary files /dev/null and b/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/customize-ui.png differ
diff --git a/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/index.mdx b/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/index.mdx
new file mode 100644
index 000000000..bc10b51fa
--- /dev/null
+++ b/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/index.mdx
@@ -0,0 +1,429 @@
+---
+title: 'Ruby quickstart'
+slug: '/getting_started/scripts_quickstart/ruby'
+---
+
+import DocCard from '@site/src/components/DocCard';
+
+# Ruby quickstart
+
+In this quick start guide, we will write our first script in [Ruby](https://www.ruby-lang.org/).
+
+
+
+
+
+
+
+
+
+
+
+
+Scripts are the basic building blocks in Windmill. They can be [run and scheduled](../../8_triggers/index.mdx) as standalone, chained together to create [Flows](../../../flows/1_flow_editor.mdx) or displayed with a personalized User Interface as [Apps](../../7_apps_quickstart/index.mdx).
+
+
+
+
+
+
+Scripts consist of 2 parts:
+
+- [Code](#code): for Ruby scripts, they can optionally have a main function. Scripts without a main function will execute the entire file.
+- [Settings](#settings): settings & metadata about the Script such as its path, summary, description, [jsonschema](../../../core_concepts/13_json_schema_and_parsing/index.mdx) of its inputs (inferred from its signature).
+
+When stored in a code repository, these 2 parts are stored separately at `.rb` and `.script.yaml`
+
+Windmill automatically manages [dependencies](/docs/getting_started/scripts_quickstart/ruby#dependencies-management) for you.
+When you use gems in your Ruby script through `gemfile` blocks (compatible with bundler/inline syntax),
+Windmill parses these dependencies upon saving the script and automatically generates a Gemfile.lock,
+ensuring that the same version of the script is always executed with the same versions of its dependencies.
+More to it, to remove vendor lock-in barrier you have ability to extract the lockfile and use it outside Windmill if you want.
+
+This is a simple example of a script built in Ruby with Windmill:
+
+```ruby
+require 'windmill/inline'
+
+gemfile do
+ source 'https://rubygems.org'
+ gem 'httparty'
+ gem 'json'
+end
+
+def main(url: "https://httpbin.org/get", message: "Hello from Windmill!")
+ response = HTTParty.get(url, query: { message: message })
+ return {
+ status: response.code,
+ body: JSON.parse(response.body),
+ message: "Request completed successfully"
+ }
+end
+```
+
+In this quick start guide, we'll create a script that greets the operator running it.
+
+From the Home page, click `+Script`. This will take you to the first step of script creation: Metadata.
+
+## Settings
+
+
+
+As part of the [settings](../../../script_editor/settings.mdx) menu, each script has metadata associated with it, enabling it to be defined and configured in depth.
+
+- **Path** is the Script's unique identifier that consists of the
+ [script's owner](../../../core_concepts/16_roles_and_permissions/index.mdx), and the script's name.
+ The owner can be either a user, or a group ([folder](../../../core_concepts/8_groups_and_folders/index.mdx#folders)).
+- **Summary** (optional) is a short, human-readable summary of the Script. It
+ will be displayed as a title across Windmill. If omitted, the UI will use the `path` by
+ default.
+- **Language** of the script.
+- **Description** is where you can give instructions through the [auto-generated UI](../../../core_concepts/6_auto_generated_uis/index.mdx)
+ to users on how to run your Script. It supports markdown.
+- **Script kind**: Action (by default), [Trigger](../../../flows/10_flow_trigger.mdx), [Approval](../../../flows/11_flow_approval.mdx) or [Error handler](../../../flows/7_flow_error_handler.md). This acts as a tag to filter appropriate scripts from the [flow editor](../../6_flows_quickstart/index.mdx).
+
+This menu also has additional settings on [Runtime](../../../script_editor/settings.mdx#runtime), [Generated UI](#generated-ui) and [Triggers](../../../script_editor/settings.mdx#triggers).
+
+
+
+
+
+Now click on the code editor on the left side, and let's build our Hello World!
+
+## Code
+
+Windmill provides an online editor to work on your Scripts. The left-side is
+the editor itself. The right-side [previews the UI](../../../core_concepts/6_auto_generated_uis/index.mdx) that Windmill will
+generate from the Script's signature - this will be visible to the users of the
+Script. You can preview that UI, provide input values, and [test your script](#instant-preview--testing) there.
+
+
+
+
+
+
+
+
+As we picked `ruby` for this example, Windmill provided some Ruby
+boilerplate. Let's take a look:
+
+```ruby
+# Builtin mini windmill client
+require 'windmill/mini'
+require 'windmill/inline'
+
+# Add your gem dependencies here using gemfile syntax
+gemfile do
+ source 'https://rubygems.org'
+ gem 'httparty', '~> 0.21'
+ gem 'json', '~> 2.6'
+end
+
+# You can import any gem from RubyGems.
+# See here for more info: https://www.windmill.dev/docs/advanced/dependencies_in_ruby
+
+def main(
+ name = "Nicolas Bourbaki",
+ age = 42,
+ obj = { "even" => "hashes" },
+ l = ["or", "arrays!"]
+)
+ puts "Hello World and a warm welcome especially to #{name}"
+ puts "and its acolytes.. #{age} #{obj} #{l}"
+
+ # retrieve variables, resources using built-in methods
+ begin
+ # Imported from windmill mini client
+ secret = get_variable("f/examples/secret")
+ rescue => e
+ secret = "No secret yet at f/examples/secret!"
+ end
+ puts "The variable at `f/examples/secret`: #{secret}"
+
+ # fetch context variables
+ user = ENV['WM_USERNAME']
+
+ # return value is converted to JSON
+ return {
+ "splitted" => name.split,
+ "user" => user,
+ "message" => "Hello from Ruby!"
+ }
+end
+```
+
+In Windmill, scripts can optionally have a `main` function that will be the script's
+entrypoint. If no main function is defined, the entire script will be executed. There are a few important things to note about the `main` function:
+
+- The main arguments are used for generating
+ 1. the [input spec](../../../core_concepts/13_json_schema_and_parsing/index.mdx) of the Script
+ 2. the [frontend](../../../core_concepts/6_auto_generated_uis/index.mdx) that you see when running the Script as a standalone app.
+- Default values are used to infer argument types and generate the UI form. String defaults create string inputs, numeric defaults create number inputs, hash/array defaults create appropriate JSON inputs, etc.
+- You can customize the UI in later steps (but not change the input type!).
+
+
+
+
+
+The first import line imports the Windmill Ruby client, which provides access to built-in methods for accessing
+[variables](../../../core_concepts/2_variables_and_secrets/index.mdx) and
+[resources](../../../core_concepts/3_resources_and_types/index.mdx).
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Back to our Hello World. We can clean up the boilerplate, change the
+main to take in the user's name. Let's also return the `name`, maybe we can use
+this later if we use this Script within a [flow](../../../flows/1_flow_editor.mdx) or [app](../../../apps/0_app_editor/index.mdx) and need to pass its result on.
+
+```ruby
+def main(name = "World")
+ puts "Hello #{name}! Greetings from Ruby!"
+ return name
+end
+```
+
+## Instant preview & testing
+
+Look at the UI preview on the right: it was updated to match the input
+signature. Run a test (`Ctrl` + `Enter`) to verify everything works.
+
+You can change how the UI behaves by changing the main signature. For example,
+if you remove the default for the `name` argument, the UI will consider this field
+as required.
+
+```ruby
+def main(name)
+```
+
+
+
+
+
+Now let's go to the last step: the "Generated UI" settings.
+
+## Generated UI
+
+From the Settings menu, the "Generated UI" tab lets you customize the script's arguments.
+
+The UI is generated from the Script's main function signature, but you can add additional constraints here. For example, we could use the `Customize property`: add a regex by clicking on `Pattern` to make sure users are providing a name with only alphanumeric characters: `^[A-Za-z0-9]+$`. Let's still allow numbers in case you are some tech billionaire's kid.
+
+
+
+
+
+
+
+
+## Run!
+
+We're done! Now let's look at what users of the script will do. Click on the [Deploy](../../../core_concepts/0_draft_and_deploy/index.mdx) button
+to load the script. You'll see the user input form we defined earlier.
+
+Note that Scripts are [versioned](../../../core_concepts/34_versioning/index.mdx#script-versioning) in Windmill, and
+each script version is uniquely identified by a hash.
+
+Fill in the input field, then hit "Run". You should see a run view, as well as
+your logs. All script runs are also available in the [Runs](../../../core_concepts/5_monitor_past_and_future_runs/index.mdx) menu on
+the left.
+
+
+
+You can also choose to [run the script from the CLI](../../../advanced/3_cli/index.mdx) with the pre-made Command-line interface call.
+
+
+
+
+
+## Dependencies management
+
+Ruby dependencies are managed using a `gemfile` block that is fully compatible with bundler/inline syntax. The gemfile block must include a single global source:
+
+```ruby
+require 'windmill/inline'
+
+gemfile do
+ source 'https://rubygems.org'
+ gem 'httparty', '~> 0.21'
+ gem 'redis', '>= 4.0'
+ gem 'activerecord', '7.0.0'
+ gem 'pg', require: 'pg'
+ gem 'dotenv', require: false
+end
+```
+
+### Private gem sources
+
+You can use private gem repositories using different syntax options:
+
+**Option 1: Per-gem source specification**
+```ruby
+require 'windmill/inline'
+
+gemfile do
+ source 'https://rubygems.org'
+ gem 'httparty'
+ gem 'private-gem', source: 'https://gems.example.com'
+end
+```
+
+**Option 2: Source block syntax**
+```ruby
+require 'windmill/inline'
+
+gemfile do
+ source 'https://rubygems.org'
+
+ source 'https://gems.example.com' do
+ gem 'private-gem-1'
+ gem 'private-gem-2'
+ end
+end
+```
+
+For authentication with private sources, specify the source URL without credentials in your script. For [Enterprise Edition](/pricing) users, add the authenticated URL to Ruby repositories in instance settings. Navigate to **Instance Settings > Registries > Ruby Repos** and add:
+
+```
+https://admin:123@gems.example.com/
+```
+
+
+
+Windmill will automatically match the source URL from your script with the authenticated URL from settings and handle authentication seamlessly.
+
+### Network configuration
+
+- **TLS/SSL**: Automatically handled as long as the remote certificate is trusted by the system
+- **Proxy**: Proxy environment variables are automatically handled during lockfile generation, gem installation, and runtime stages
+
+Windmill will automatically:
+- Parse your gemfile block when you save the script
+- Generate a Gemfile and Gemfile.lock
+- Install dependencies in an isolated environment
+- Cache dependencies for faster execution
+
+
+
+
+
+
+
+
+
+## Caching
+
+Every gem dependency in Ruby is cached on disk by default. Furthermore if you use the [Distributed cache storage](../../../misc/13_s3_cache/index.mdx), it will be available to every other worker, allowing fast startup for every worker.
+
+## What's next?
+
+This script is a minimal working example, but there's a few more steps that can be useful in a real-world use case:
+
+- Pass [variables and secrets](../../../core_concepts/2_variables_and_secrets/index.mdx)
+ to a script.
+- Connect to [resources](../../../core_concepts/3_resources_and_types/index.mdx).
+- [Trigger that script](../../8_triggers/index.mdx) in many ways.
+- Compose scripts in [Flows](../../../flows/1_flow_editor.mdx) or [Apps](../../../apps/0_app_editor/index.mdx).
+- You can [share your scripts](../../../misc/1_share_on_hub/index.md) with the community on [Windmill Hub](https://hub.windmill.dev). Once
+ submitted, they will be verified by moderators before becoming available to
+ everyone right within Windmill.
+
+Scripts are immutable and there is a hash for each deployment of a given script. Scripts are never overwritten and referring to a script by path is referring to the latest deployed hash at that path.
+
+
+
+
+
+For each script, a UI is autogenerated from the jsonschema inferred from the script signature, and can be customized further as standalone or embedded into rich UIs using the [App builder](../../7_apps_quickstart/index.mdx).
+
+
+
+
+
+
+In addition to the UI, sync and async [webhooks](../../../core_concepts/4_webhooks/index.mdx) are generated for each deployment.
+
+
+
+
diff --git a/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/ruby-gems-instance-settings.png b/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/ruby-gems-instance-settings.png
new file mode 100644
index 000000000..3231fbcb3
Binary files /dev/null and b/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/ruby-gems-instance-settings.png differ
diff --git a/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/ruby-settings.png b/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/ruby-settings.png
new file mode 100644
index 000000000..f49fc1ca9
Binary files /dev/null and b/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/ruby-settings.png differ
diff --git a/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/ruby-startpage.png b/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/ruby-startpage.png
new file mode 100644
index 000000000..9c36cdbd3
Binary files /dev/null and b/docs/getting_started/0_scripts_quickstart/14_ruby_quickstart/ruby-startpage.png differ
diff --git a/docs/getting_started/0_scripts_quickstart/index.mdx b/docs/getting_started/0_scripts_quickstart/index.mdx
index 40980e550..04cc227c8 100644
--- a/docs/getting_started/0_scripts_quickstart/index.mdx
+++ b/docs/getting_started/0_scripts_quickstart/index.mdx
@@ -11,13 +11,14 @@ import {
SiAnsible,
SiRust,
SiCplusplus,
+ SiRuby,
} from 'react-icons/si';
import { LiaJava } from "react-icons/lia";
# Scripts quickstart
-Windmill supports scripts in TypeScript, Python, Go, PHP, Bash, C#, SQL and Rust.
+Windmill supports scripts in TypeScript, Python, Go, PHP, Bash, C#, SQL and more.
+
diff --git a/docs/misc/17_windows_workers/index.mdx b/docs/misc/17_windows_workers/index.mdx
index e97a4067f..b12304885 100644
--- a/docs/misc/17_windows_workers/index.mdx
+++ b/docs/misc/17_windows_workers/index.mdx
@@ -149,3 +149,11 @@ You can also check other installation methods in uv`s [official documentation](h
# Make sure you provide **full** path!
$env:COURSIER_PATH="C:\..\..\coursier"
```
+
+### Ruby executor
+
+1. **Install Ruby**: For Ruby you will need to have `ruby.exe`, `bundler.bat` and `gem.cmd` in `PATH`. You can use [RubyInstaller](https://rubyinstaller.org/) for this.
+ - Start PowerShell and verify you have working executables:
+ ```powershell
+ ruby --version && bundler --version && gem --version
+ ```
diff --git a/sidebars.js b/sidebars.js
index b2964445b..4cccf5ea0 100644
--- a/sidebars.js
+++ b/sidebars.js
@@ -84,6 +84,11 @@ const sidebars = {
type: 'doc',
id: 'getting_started/scripts_quickstart/java_quickstart/index',
label: 'Java'
+ },
+ {
+ type: 'doc',
+ id: 'getting_started/scripts_quickstart/ruby_quickstart/index',
+ label: 'Ruby'
}
]
},
diff --git a/static/images/ruby-startpage.png b/static/images/ruby-startpage.png
new file mode 100644
index 000000000..dc4352856
Binary files /dev/null and b/static/images/ruby-startpage.png differ
diff --git a/static/videos/ruby.mp4 b/static/videos/ruby.mp4
new file mode 100644
index 000000000..67d7d2f02
Binary files /dev/null and b/static/videos/ruby.mp4 differ