Ruby to EXE – Turn ruby scripts into portable executable apps

This method works with:

  1. Rails apps (your executable file will be a portable webserver);
  2. Scripts with gems;
  3. Multiple source file scripts;
  4. Simple, single-source file scripts;

The final app is a single executable file that can run on systems without a ruby interpreter installed.


I have developed a gem that automates this process. Check it out:


gem install rb2exe
echo "puts 'Hello world'" > test.rb
rb2exe test.rb

Now, how this works, step-by-step

1. First, let’s create a simple Hello World app:

mkdir hello
cd hello
echo "puts 'Hello world'" > test.rb
ruby test.rb
(out)Hello World

The way this project is transformed into a self-contained executable file is by using Ruby Traveler. Ruby Traveler is just a folder with a standalone “ruby” executable. The idea is to add Ruby Traveler’s folder and binaries into your project and compress everything into a single self-extractable zip file. This self-extractable file extracts the compressed data into a temporary folder and executes the main ruby script (ex. “test.rb”).

As this is based on Ruby Traveler (by Phusion), which seems to be a stalled project, we are locked to the only versions it supports – the latest one is 2.2.2.

If you want to use a different Ruby version, other than 2.2.2, I recommend adapting this script to zw693’s Traveling Ruby, which supports ALL Ruby versions:

Part I: Standalone Ruby

2. Your ruby version should be 2.2.2, 64 bits:


If your version is different, please install the 2.2.2 (e.g. rvm use 2.2.2).
This will NOT work with 2.2.0, 2.2.3, 2.1, etc.

3. Duplicate the project folder, naming it as “app”:

cd ..
cp -r -pa hello app

4. In the project folder, create a “.package/payload/lib/ruby” sub-folder:

cd hello
mkdir -p .package/payload/lib/ruby

5. Move the “app” folder to “.package/payload/lib”:

mv ../app .package/payload/lib
tree -a
(out)├── .package
(out)│   └── payload
(out)│       └── lib
(out)│           ├── app
(out)│           │   └── test.rb
(out)│           └── ruby
(out)└── test.rb

6. Download ruby traveler 2.2.2 64 bits (5.6M), and unzip it on “.package/payload/lib/ruby”:

cd .package/payload/lib/ruby
tar -xf traveling-ruby-20150715-2.2.2-linux-x86_64.tar.gz
rm traveling-ruby-20150715-2.2.2-linux-x86_64.tar.gz

7. Go back to “.package/payload/”:

cd ../..

8. Create a wrapper script (name it as “installer”):

nano installer

set -e

# Figure out where this script is located.
SELFDIR="`dirname \"$0\"`"
SELFDIR="`cd \"$SELFDIR\" && pwd`"

if [ -f "$SELFDIR/lib/vendor/Gemfile" ]
  # Tell Bundler where the Gemfile and gems are.
  export BUNDLE_GEMFILE="$SELFDIR/lib/vendor/Gemfile"

  # Run the actual app using the bundled Ruby interpreter, with Bundler activated.
  exec "$SELFDIR/lib/ruby/bin/ruby" -rbundler/setup "$SELFDIR/lib/app/test.rb"
  exec "$SELFDIR/lib/ruby/bin/ruby" "$SELFDIR/lib/app/test.rb"

chmod +x installer

Replace the “exec” line with your actual command to start the application. Eg. for Rails apps, it should be:
RAILS_ENV=production exec "$SELFDIR/lib/ruby/bin/ruby" -rbundler/setup "$SELFDIR/lib/app/bin/rails" server

PS: The previous script is based on the “traveling ruby” tutorial.

Part II: Gemfile

If your project has a Gemfile, you need to follow these extra steps:

9. Create a “tmp” folder on “.package/payload/lib”:

cd lib
mkdir tmp
cd tmp

10. Copy the project Gemfile to tmp:

cp ../app/Gemfile* .

11. Download gems into the “lib/vendor” folder:

BUNDLE_IGNORE_CONFIG=1 bundle install --path ../vendor --without development

12. Delete tmp folder:

cd ..
rm -Rf tmp

13. [Optional] Delete gem’s cache in the vendor folder:

rm -f vendor/*/*/cache/*

14. Copy the Gemfile to the vendor folder:

cp app/Gemfile* vendor/

15. Create a bundler config

mkdir vendor/.bundle/
cd vendor/.bundle/
nano config

BUNDLE_WITHOUT: development

Part III: Pack everything as a single self-extract file

This part is based on Jeff Parent’s article.

17. Create a script to decompress everything

export TMPDIR=`mktemp -d /tmp/selfextract.XXXXXX`

ARCHIVE=`awk '/^__ARCHIVE_BELOW__/ {print NR + 1; exit 0; }' $0`

tail -n+$ARCHIVE $0 | tar -xz -C $TMPDIR


cd $CDIR
rm -rf $TMPDIR

exit 0


18. Package builder

cd payload
tar cf ../payload.tar ./*
cd ..

if [ -e "payload.tar" ]; then
    gzip payload.tar

    if [ -e "payload.tar.gz" ]; then
        cat decompress payload.tar.gz > output
        echo "payload.tar.gz does not exist"
        exit 1
    echo "payload.tar does not exist"
    exit 1

chmod +x output
echo "Self-extract file created"
exit 0

chmod +x decompress
chmod +x build
(out)Self-extract file created

And that’s it. You can now rename and distribute the generated “output” file :)

Close Menu