Using Rootbox

Restrictions

Rootbox requires two things:

  • A Linux system. Some others Unixes (like the BSDs) may work, but they’re untested.
  • An file system with sparse file support to store the workspace directory (see below). Rootbox relies on the magic of sparse files to easily create boxes and images. (Note that only ext4 has been tested.)

Also, here’s something import:

ROOTBOX IS NOT SECURE.

Let me repeat:

ROOTBOX IS NOT SECURE.

Third time’s the charm:

ROOTBOX IS NOT SECURE.

Rootbox’s “boxes” are not intended to be used like you would use containers. They are designed to be created and easily distributed and portable development environments, but they are not designed for running web servers or untrusted code. Use containers for that!

Getting Started

Just run:

curl -L https://goo.gl/H3OpCL | sudo sh

If you’re not confortable with running random internet scripts, you can also install it from GitHub instead:

git clone https://github.com/project-rootbox/rootbox.git
cd rootbox
sudo sh install.sh

Once that’s done, you need to initialize the Rootbox workspace directory, which is where all your images and boxes will be stored. You can do so using rootbox init:

rootbox init

This will initialize Rootbox in $HOME/.rootbox. If you want to use a different directory, just say so:

rootbox init my_directory

This will create the directory and symlink it to $HOME/.rootbox.

Note that, as already mentioned above, this directory must be on a file system with sparse file support!

If at any point in the future, you want to use a different workspace directory, you’ll need to pass -f to force the reinitialization.

Warning

Do NOT tamper with the workspace directory! By that, I mean don’t go touching it in any way. Remember when I said that Rootbox uses sparse files? Well, that means that, if you try to play with the directory or copy/move files outside of it, there’s a chance all the sparse files will inflate to their full 128 GB size. Ouch!!

Concepts

Before we continue, I’d like to introduce some terms you’ll see floating around this document:

  • workspace: As mention above, this is the directory where Rootbox will save all your images and boxes (see below) into.
  • boxes: Boxes are essentially Rootbox’s version of containers. They hold an Alpine Linux installation, as well as whatever else you want to put in them. Note that, unlike containers, boxes are intentionally insecure, and they aren’t intended to be used for everything that one might use containers for.
  • images: Every box is base on an image. Images are just vanilla, unmodified Alpine Linux installations.
  • bind mounts: These allow you to “mount” external directories inside your boxes.

Got that? Let’s go on!

Creating an image

As already mentioned, images are unmodified installations of Alpine Linux, ready to be converted to a box when desired. They’re easy to create:

rootbox image.add 3.5

Here, we’re using Alpine Linux 3.5, as passed to the version argument. Note that the resulting image will be around 210 MB in size. This seems rather big, but it also contains several development tools and libraries, such as GCC, libstdc++, git, and more. If you want just a plain Alpine Linux installation, then you can also pass -s:

rootbox image.add 3.5 -s

This will create a slim or nodev image, which is significantly lighter (around ~13 MB in size) but doesn’t contain development tools inside. When you create a slim image, you can later reference using VERSION-nodev, like 3.5-nodev.

(You can also use image.list to list all your installed images and image.remove -v VERSION to remove one of your images.)

Creating a box

Creating a new box is easy:

rootbox box.new mybox

Here, we’re creating a box called mybox, using the Alpine Linux 3.5 image that was created earlier. If you want to use a different image instead, use -v:

rootbox box.new mybox -v 3.4

(To use the slim/nodev image, pass -v 3.5-nodev instead.)

You can also pass a list of bind mounts to be mounted whenever the box is run. For example:

rootbox box.new mybox outside_directory///inside_directory

Now, whenever this box is run outside_directory will appear inside it as /inside_directory. For instance, if you always want the current directory to be mounted inside the box as /cwd, you could run:

rootbox box.new -n mybox .///cwd

Absolute paths can be used, too:

rootbox box.new -n mybox /home/$USER///external_home

Running your boxes

Now that a box has been created, let’s run it!

rootbox box.run mybox

This will put you inside an ash shell inside your box. Take a look around for a bit! Once you’re done, you can Ctrl-D out of it.

Just like above, bind mounts can be created when the box is run:

rootbox box.run mybox .///cwd

These will be mounted in addition to any specified when creating the box for the first time.

While inside the box, you can also install packages using the Alpine package manager, apk, like this:

sudo apk add clang

A command can also be passed via the command line:

rootbox box.run mybox -c 'echo 123'

If you’re planning on running X11 applications inside your box, you’ll need to pass -x:

rootbox box.run mybox -x

(Note that you may also need to run sudo apk add font-adobe-100dpi if you run into problems involving missing fonts, e.g. you get boxes instead of text in your programs.)

Box factories

Factories are an imporant concept in Rootbox! A box factory is just a shell script that’s run inside your box upon creation to set things up. For instance, you could create a factory clang.sh containing:

sudo apk add clang

To create a box using your factory, you can just run:

rootbox box.new mybox -f clang.sh

-f takes a path to your box factory. However, things get fancier than that!

You can have one factory depend on another one. For instance, you might have llvm.sh to install llvm:

sudo apk add llvm

Then, clang.sh could be modified to read:

#:DEPENDS llvm.sh
sudo apk add clang

The #:DEPENDS means that llvm.sh must be run first. Now, when you use clang.sh as your box factory, llvm.sh will be run, too!

Box factories can also specify Alpine Linux versions that they work on:

#:VERSION 3.5 3.5-nodev

This factory will run under 3.5 and 3.5-nodev, but if you try to use it on an Alpine 3.4 box, Rootbox won’t let you.

Using factories from the internet

If things weren’t already awesome enough, you can load your factories straight from the web or Git. If you have a factory up at GitHub, you could use it via:

rootbox box.new mybox -f git:myuser/myrepo@@mybranch///myfactory.sh

If @@mybranch is ommited, it defaults to master. If ///myfactory.sh is ommited, it defaults to factory.sh. For instance, to load factory.sh from CoolRootboxScripts/cool_scripts_set_1:

rootbox box.new mybox -f git:CoolRootboxScripts/cool_scripts_set_1

To use my_other_factory.sh:

rootbox box.new mybox -f git:CoolRootboxScripts/cool_scripts_set_1///my_other_factory.sh

To use it from the branch devel:

rootbox box.new mybox -f git:CoolRootboxScripts/cool_scripts_set_1@@devel///my_other_factory.sh

GitLab is supported, too:

rootbox box.new mybox -f gitlab:MyGitlabUser/my_gitlab_repo@@branch///factory_name.sh

as well as any other plain old Git repository:

rootbox box.new mybox -f git:https://whatever.com/my_repo.git@@branch///factory_name.sh

In fact, factories can be pulled from anywhere on the internet:

rootbox box.new mybox -f url:https://mysite.com/some_cool_factory.sh

The syntax this time is a bit different: the url must point to an absolute URL to the factory. If the URL ends with a slash (/), then factory.sh will be appended to it.

These location formats can be used inside the DEPENDS section of a script, too. You could have something like this:

# This is my cool factory script!
#:DEPENDS git:myuser/myrepo///myfactory.sh
#:DEPENDS url:rootbox_factories.com/myotherfactory.sh

There are several pre-made factories in the still-growing rootbox-factories <https://github.com/project-rootbox/rootbox-factories> repository. If you have a really cool idea for a factory that you figure would be useful to a lot of people, you can open up an issue there as a request.

Managing, exporting, and importing your boxes

If you want to see a list of all the boxes that have been installed, just run:

rootbox box.list

You can get info about an individual box with box.info:

rootbox box.info mybox

Boxes can be cloned:

rootbox box.clone source_box new_box

and deleted:

rootbox box.remove mybox

If you want to change your a box’s settings later on, you can use the box.update command set. For instance, you can use box.update.binds to update a box’s default bind mounts:

rootbox box.update.binds mybox a///b   # Add a///b to the default bind mounts.
rootbox box.update.binds mybox ^a///b  # Remove it from the default bind mounts.

As you can see, prefixing a bind with ^ will remove it instead of adding it.

Similarly, box.update.factory can be used to run another factory once the box has already been created:

rootbox box.update.factory mybox url:https://foo.bar/myfactory.sh

Exporting and importing your boxes

Boxes can be exported using box.dist. It works much like you’d expect by now:

rootbox box.dist mybox

The default file name is <your_box_name>.box. In this case, it’ll be mybox.box. That can be overriden, of course:

rootbox box.dist mybox -o my_custom_name.box

In addition, you can apply compression using -c to make it a bit smaller:

rootbox box.dist mybox -o gzip_compressed.box.gz -c gzip
rootbox box.dist mybox -o bzip2_compressed.box.bz2 -c bzip2

Boxes can also be imported:

rootbox box.import mybox.box.gz mybox

Here, mybox.box.gz is being imported using the name mybox. In fact, boxes can be imported from virtually anywhere, using the exact same syntax as used with box factories:

rootbox box.import url:rootbox_storage.com/1234567 mybox
rootbox box.import git:Cooluser101/myboxes///cool_stuff.box mybox

Some examples

Building C

Donald wants to create a box that can be used to statically compile his C programs. Alpine Linux is great for static linking:

rootbox box.new static_c .///cwd
rootbox box.run static_c

# Inside the box...
cd /cwd
echo 'int main() {}' > x.c
gcc -static -o x x.c
exit

# Back outside again...
ldd x  # statically linked

Building Nim

Mary wants to create a box designed for building Nim programs. She can use factories to automate…everything:

sudo apk add xz linenoise-dev libexecinfo-dev

curl -L https://nim-lang.org/download/nim-0.16.0.tar.xz -o nim.txz
tar xvf nim.txz
cd nim-0.16.0
./build.sh

bin/nim c koch
./koch boot -d:release -d:useLinenoise
sudo ./koch install /usr/local/bin

She can save this to nim-factory.sh. Then, to create her image, she can just run:

rootbox box.new nim .///cwd -f nim-factory.sh

If she uploads her Nim factory to GitHub in mary123/factories, someone else can use it, too:

rootbox box.new nim .///cwd -f git:mary123/factories///nim-factory.sh

If Robby wants to build a Nim program using Rootbox, he can create a factory, too:

#:DEPENDS git:mary123/factories///nim-factory.sh
git clone https://github.com/bobby456/my-nim-program.git
cd my-nim-program
nim c my-program.nim
sudo cp my-program /usr/local/bin

Closing thoughts

Rootbox is still in the beta stages. If you notice anything isn’t working quite correctly, feel free to report it to the GitHub repo.

Have fun playing with your boxes!!!