Tutorial: IIIF on RPi

Authors: Wout Dillen and Joshua Schäuble
First published: 26 June 2019
Last update: 6 August 2019
(first published version) DOI: http://dx.doi.org/10.17613/6n9b-2n48

This tutorial was developed by Wout Dillen and Joshua Schäuble at the Centre for Manuscript Genetics (CMG), as part of the IIIF courses of the University of Antwerp‘s Summer School on Digital Humanities. This is a one-week summer school organized by the Antwerp Centre for Digital humanities and literary Criticism (ACDC).

The course was first taught in Antwerp in 2018 (3-7 September), and an adapted version was taught again in 2019 (1-5 July). In the course, students learn how to set up a IIIF-compatible image server (iipimage) on a local network of Raspberry Pi computers, to ultimately re-use and manipulate each other’s images using the IIIF protocol.

1. Double-Checking Your Raspberry Pi’s Setup

Developed as a classroom instrument, this tutorial assumes that you are a student, and that you have access to a Raspberry Pi (RPi) with the Raspbian Operating System pre-installed on it. While they were preparing your RPi for this course, your tutors will also have checked that they are set up correctly for the course. Still, it is important that we double-check some of their settings, because they are crucial for the rest of this course.

1.1 Make sure that SSH is enabled on your RPi

On your RPi, select the raspberry icon in the top left corner, and navigate to: Preferences > Raspberry Pi Configuration. Here, select the tab Interfaces and ensure, that SSH is Enabled.

1.2 Ensure that the keyboard layout is set correctly

Up until the moment when we are able to SSH into our RPis, we will need to use the keyboard to control them. Go to Preferences/Mouse and Keyboard Settings, select the tab Keyboard and there the button Keyboard Layout. Select the layout of the keyboard you are using, in our case (at the University of Antwerp) this would be Belgium/Belgian.

1.3 Ensure that the system language is set to English

We want all RPis to be operated in English. Go to Preferences > Raspberry Pi Configuration again, this time select the tab Localisation. Click on Set Locale and make sure the Language is set to English and the Character Set is UTF-8.

1.4 Set the System Password

Although we are in a closed network for this course and do not expect any hacking attempts, it is always good practice to change your RPi’s password. Especially when SSH is enabled, changing the default password (raspberry) will prevent others from accessing and controlling your computer without your knowledge. Go to Preferences > Raspberry Pi Configuration again, this time select the tab System and set the password to your own (unless your tutor provides you with one).

1.5 Reboot your RPi

Go to your terminal, and enter the following command:

sudo reboot now

2. Connect your RPi to the Internet

In the original setup for this tutorial, all RPis are connected to one another (and together to a local router, called IIIFarm) via ethernet. But we need them to be connected to the actual Internet too – to download packages etc. For this connection we will use the WiFi. The problem here is, that your RPi will prioritise Ethernet over WiFi for its internet connection. This is not surprising, because ethernet connections are typically much faster and more reliable than WiFi connections. But so in our case, we’ll need to turn this around, so the RPi will always use WiFi for internet connections first, rather than trying to connect through our (local, unconnected) ethernet network.

2.1 Connect your RPi to the WiFi

WiFi network names and passwords will differ depending on where you are following this course, so listen to your tutors, and use the login information they provide.

2.2 Prioritise WiFi for web requests using nano

So now we want to make sure our RPi uses the WiFi to connect to the internet, rather than its ethernet connection. To change this, we will need to make changes in a configuration file. Open a terminal, and enter:

sudo nano /etc/dhcpcd.conf

This will tell the RPi to use the programme nano, a shell text editor, to open a file located in the path /etc/dhcpcd.conf.

2.3 Assign metric values to your ports

Use the arrow-down key to move to the end of the configuration file. There, add the following lines:

interface wlan0
metric 202

interface eth0
metric 303

Hit Ctrl+o to save the file and confirm with enter. These lines assign metric values to your WiFi and your ethernet ports. The lower the value, the higher its priority. So, since 202 is lower than 303, wlan0 (your WiFi port) will be rated higher than eth0 (your ethernet port) and our system will send web requests via wlan0 instead. Finally, hit Ctrl+x to leave nano.

2.4. Reboot your RPi

To allow these changes to take effect, you will now have to reboot your RPi again. We will do this using the terminal:

sudo reboot now

2.5 Connect the RPi to the WiFi network again

Open the browser. You should be directed to the university’s login page now. This means that assigning the metric values has worked! Each group has received credentials for an account on the guest network. Log in using these access data. Once you are logged in, visit any website to make sure your internet is connected.

2.6. Double-check the metric values

Open a terminal and enter the following command:


This command will show you your routing table: a list of all the system’s routing destinations (in the form of IP addresses), that tell the system where to send which data packages. Here you can double-check that the metric value of the wlan0 interface is now lower than that of the eth0 interface.

2.7 Note down your IP Address

As mentioned, we will need the IP address that our router has assigned to our RPi to SSH into the RPi via our laptops. We can find the IP address we need by running the following command:


This lists all your network settings. In the list, find the entry that starts with eth0 (typically the first one), and note down the first IP address that you see, the one that directly follows the word inet.

2.8. No more peeking!

Now that we have the IP addresses of our RPis, we can finally access them via SSH, using our laptops. That means we will no longer need to look at its desktop. Turn off the monitor that is connected to your RPi.

3. SSH into your RPi

We will now start to control our RPi from a different system (your laptop) using the command line. Since the rest of this tutorial assumes that you know some basic command line commands (and will even ask to write your own commands here and there), this is the point where your tutors will offer you a brief introduction to working in a Linux terminal. At a later point in time, a similar tutorial for training command line basics will be provided and linked here on this website.

3.1 SSH Connect

On your laptop (the one that is connected to the IIIFarm WiFi) open a Command Line Interface. Here, enter:

ssh pi@your.ip.address

and confirm with enter.

On your first login to a new resource you will get a security warning. Confirm by typing yes. You will be prompted for your RPi’s password. Type in the password that you’ve set up for your RPi, and confirm with enter.

3.2 Creating Files and Directories

At this point, your tutors should have either already put a folder on your RPi’s Desktop, or prepared a USB-stick that passes around while you complete this exercise. In the second case, when you get the USB, you can turn your RPi’s monitor on, plug the USB into the RPi, and copy the directory with your RPi’s name onto your Desktop. (The whole directory, not just the files that are inside). When this is done, turn the monitor off again, and continue with the following exercise.

3.3 Command Line Exercise

  • Navigate to the Desktop
  • Create a new directory called html
  • Navigate into that directory
  • Create a new document in that folder, called index.html
  • Also create a new directory in that folder, called images
  • Verify that your html directory now containes an index.html file and an images subdirectory

To continue beyond this point, you will need the folder provided by your tutors to be copied to your Desktop. So wait for the USB if necessary.

  • Check your Present Working Directory
  • Navigate back to the Desktop
  • Verify that the Desktop has two directories: html and one with your_RPi_name
  • Navigate into the directory with your_RPi_name
  • List the contents of this directory

As this list will show, the directory contains 2 .png images, and one .jpg image. The .png images are high-resolution facsimile images of random pages of the first draft of Mary Shelley’s Frankenstein. For this tutorial, they are your institution’s pieces of the Monster we are creating.

  • Copy these contents into the following directory: /home/pi/Desktop/html/images
  • Navigate into that directory, and verify that it indeed contains these three images
  • Rename the .png images using mv. Their new names should be image1.png and .image2.png

3.4 Create a HTML document in nano

First, navigate back to the htmldirectory on your Desktop. Now open the index.html file with nano:

 nano index.html

Now you can use the command line text editor nano to write the following code into the file:

   <meta charset="utf-8" />
   <title>My Institution's Page</title>
   <h1>Welcome to [your_RPi_name]</h1>
   <p>See our beautiful pieces of the Monster:</p>
   <img src="images/frankenstein.jpg" style="width:300px;" />

3.5 Creating a HTML Document in nano

The code we just wrote in nano is written in a language that your browser can read: html. This means that if you open it in your browser, it will display the document as if it were an online web-page.

In the code, the following line will load an image from your html/images directory, and resize it to a width of 300px in your browser:

<img src="images/frankenstein.jpg" style="width:300px;" />

When you’re satisfied that the file is copied correctly into your command line through nano, save the file with Ctrl+o, and quit nano with Ctrl+x.

4. Set up a Web Server on Your RPi

Now that everything’s set up and we can connect to the internet, we’ll start with setting up a web server on your RPi. This will allow you to serve your content (the offline web page you’ve just built) with other users on your network (here, the IIIFarm) on an actual website.

4.1 Update APT Package List

Before we start downloading and installing packages on our RPi, it’s good practice to first update apt-get‘s package list – the list of links to the public repositories that our operating system’s app store uses to know where the most up-to-date versions its software packages are hosted. To do that, enter:

sudo apt-get update

4.2 Install Apache 2 (with fcgid)

Now it’s time to download our software! The web server that we’re going to use is the commonly used Apache2 webserver. In addition, we also need to install one of Apache2‘s modules (a kind of sub-software-package) called fcgid.

With apt-get install, you can line up a series of software packages to install them all with a single command. So go ahead and run:

sudo apt-get install apache2 libapache2-mod-fcgid

On running this command, apt-get will inform you if the software has any dependencies (other software that needs to be downloaded), and tells you how much disk space these downloads will take up on your RPi. You’ll have to confirm with y, and wait until your packages are downloaded and installed. As we’re all on the same network and the RPi’s aren’t very fast, this may take a while.

4.3 Start Web Server

Once the software is downloaded, we can start it up on the RPi. Run:

systemctl start apache2

Here, you will be asked which user you want to use to run the Apache2 service. Choose 1 (pi) and confirm with enter. The command line will then prompt you for a password, so type in your RPi’s password and confirm by hitting enter again.

The command we’ve just run – systemctl – is a utility to manage (start, stop, restart) services on your RPi (like the Apache2 service we’ve just installed). We’ll be using it a lot in this tutorial, so make sure you remember your RPi password!

4.4. Test Apache

Before we do anything else, we’d like to make sure that our Apache webserver is actually running. On your computer, open a browser, enter your RPi’s IP Address into the browser’s address bar, and confirm with enter. You should now see the Apache2 Debian Default Page. On the top of that page, in a red banner, you’ll read “It works!”

4.5. Viewing Your Website

By default only contents that are in the folder /var/www/html/ are accessible. Although this folder could be changed (e.g. to your Desktop, or a folder in your Documents) doing so is not considered good practice. Instead of relocating the folder Apache points to, we move our content (those files we want to access via the browser) to the default folder.
Copy the html folder from the Desktop to /var/www:

sudo cp -r /home/pi/Desktop/html/ /var/www/

Now you can enter your IP address into the browser of your laptop. If everything went well, you can load the website from your RPi (which now functions as your server) on your laptop (which funtions as the client).

5. Install the IIPImage Server on Your RPi

In the previous step, we installed Apache – a web server that allows each of us to host web pages such as our own page in html) and share them within our network. As we’ve seen, we can easily hard-code images into such html web pages (by providing a link to the image in the @src attribute of an <img> tag) – but that is not enough for our purposes. Instead, we need to install a dedicated image server alongside our web server, to process the images on the fly. For that, we will install a IIIF-compliant image server called IIPImage.

5.1 Install the IIPImage-Server

Install the package iipimage-server by entering the following command:

sudo apt-get install iipimage-server

You will have to download some dependencies (between 50-100MB), confirm with y and wait until the image server is installed.

5.2 Change the image server’s data directory

By default, the data directory of the image server is under /usr/lib/iipimage-server/. We want to change this to /var/www/iipimage-server/. To do so, we have to copy the folder over to /var/www/ and tell the image server where the data directory has moved. Copy the iipimage-server directory from /usr/lib/ to /var/www:

sudo cp -r /usr/lib/iipimage-server/ /var/www/iipimage-server/

To use the image server in our website, we need to run it as an Apache module. Such modules are configured in the directory /etc/apache2/mods-available/Change to this directory. Once you are there, we need to open the image server’s config file with nano so we can make some changes (don’t forget the sudo!). the config file is called iipsrv.conf.

sudo nano /etc/apache2/mods-available/iipsrv.conf

In this file change the following line:

ScriptAlias /iipsrv/ "/usr/lib/iipimage-server/"


ScriptAlias /iipsrv/ "/var/www/iipimage-server/"

Save the file with Ctrl+o, and hit enter to confirm) and close nano (Ctrl+x). Now once we enable the module, apache knows where we put the image server’s data directory, and the two servers can work together.

5.3 Enable the necessary Apache modules for the image server

First, we want to double check that the fcgid module is enabled. Since we installed it earlier, this should already have happened by default. Enter:

sudo a2enmod fcgid

You should now see a message that confirms that fcgid was already enabled. Next, we need enable the headers module. Enter:

sudo a2enmod headers

If one of these two modules was not enabled before, you will have to restart apache now. In that case, enter:

systemctl restart apache2

Finally, we’ll want to check if our image server’s module (iipsrv) is enabled too. Enter:

sudo a2enmod iipsrv

Now that all three modules are definitely enabled, we need to restart Apache again. Enter:

systemctl restart apache2

5.4. Enable CORS

In the image servers configuration file we have to enable “cross origin resource sharing” (CORS). It is important to enable this setting to make sure the image server is IIIF-compliant, because it allows others to embed your images into their website. To enable CORS, open the apache configuration file with nano.

sudo nano /etc/apache2/apache2.conf

Move down (arrow-down key ) to the end of the file and add the following line:

Header set Access-Control-Allow-Origin *

Make sure there are no spelling mistakes in this line! Save the file (Ctrl+o, enter) and leave nano (Ctrl+x).

And, again, Apache needs to be restarted:

systemctl restart apache2

5.5 Check if your IIPImage Server is running

Finally, we’ll want to make sure that everything is working correctly, and that the image server we just configured is actually running. In your laptop’s browser go to:


If the IIPImage Server start screen appears, your server should run correctly. Well done!

6. Making Pyramid TIFFs for IIPImage

IIPImage needs a specific type of files to work its image serving magic. These are pyramid TIFFs – which are basically layered documents that contain multiple, mapped versions of the same image in different resolutions. This allows the image server to optimise its zoom function, as it switches to higher resolution images as the user zooms deeper and deeper into the document.

6.1. Install Image Processor: VIPS

To convert our high-resolution images into Pyramid TIFFs, we will first need to install another package on our RPi – this time we need an image processor. There are a couple of different options available for this, but we’ll use the software package VIPS. Use apt-get to install the package libvips-tools:

sudo apt-get install libvips-tools

6.2. Find Your Images

Change to the folder /var/www/html/images/. At an earlier stage, we stored two Frankenstein facsimile images in this folder: image1.png and image2.png. Make sure they are still there using ls.

6.3. Transform Your Images

To transform these .png images into (tiled pyramid) .tif images, run the following command:

sudo vips im_vips2tiff image1.png image1.tif:deflate,tile:256x256,pyramid

Once you’ve hit enter, wait until the processing is done. This can take several minutes, because Pyramid TIFFs are huge files.

Once the first image is processed, move on to the second image. Afterwards make sure, both .tif files (image1.tif and image2.tif) are in the folder, alongside to the .png files – which should still be there too.

6.4. Copy the Pyramid TIFFs to the Image Server’s Data Directory

Just like Apache stores the accessible documents in a specific data directory (/var/www/html), our IIPImage server also has a directory where our images need to be stored. Earlier, we’ve set this directory to /var/www/iipimage-server/. In order to make our images accessible via the image server module (and not only via apache) we have to copy (cp) them into this folder.

sudo cp image*.tif ../../iipimage-server/.

Note: ../../ only works as long as you are in the /var/www/html/images/ folder. It would not lead to the correct target from any other pwd. An alternative would be to use an absolute path.

7. IIIF Parameters

Now that we have set up IIPImage, you can access all of the images in your var/www/iipimage-server directory in any browser or IIIF-compliant image viewer – such as Mirador, Universal Viewer, OpenSeadragon, or Leaflet-IIIF.

7.1. Accessing Images in the Browser

When you call an image via an IIPImage server URL, the image server will show you this image according to some parameters that you have set in the URL. Let’s have a look at how this works.

Open your browser (on the laptop!) and go to:


Now, open another tab in your browser and go to:


Both URLs show you the same image, but not in the same way. Compare the URLs above and try to find out how it influenced the server’s presentation of that image in your browser.

7.2 Experiment with the IIIF Image API

The IIIF consortium provides a detailed documentation of the IIIF Image API’s parameters here. Have a look at this documentation, and play with different settings for your images by manipulating the URL in the following exercise:

Reading up on the IIIF parameters, find out how to:

  • Rotate your image by `180` degrees
  • Retrieve **only a section** of an image
  • Retrieve an image with a width of `5000px` and a proportionally scaled height
  • Retrieve an image with a `width` of `100px` and a `height` of `300px`
  • Display your image in `greyscale`

7.3. Get the `info.json` from your image server

All the information you need to build tools that show your images work on the basis of a info.json file, which is generated by the server for every image. To view this file for your own collection, enter the following URL into your browser:


When no further IIIF URL variables are specified, the IIPImage server will forward you to the info.json file.

7.4 Accessing Each Other’s Images

So, each of us now has a fully functioning RPi with a web server and an image server that provide access to a couple of tiled pyramid TIFF images that we have learned to access and manipulate] with the IIIF URL parameters. And since all of our RPis are connected on the same network, we can use the same technology that allows us to view and manipulate the images on our own RPi in the browser of our own laptops allow us to do the same with each other’s images too.

Ask your neigbour to give you the information you need to build IIIF compliant URLs for their images. Then try to view and manipulate their images using the IIIF parameters

We will need URLs like these going forward. So save some of these URLS on your laptop somewhere (like a .txt or .doc file), so you will be able to copy and paste them.

8. Reusing IIIF Images

Now that we have a series of links to images (our own as well as our neighbours’), we can embed their URLs into our html page to incorporate them in our websites.

8.1. Include Images in your html Web Page.

Open the index.html file we created earlier with nano:

sudo nano /var/www/html/index.html

In this file, add the following two lines after the frankenstein.jpg image:

<br/><img src="your.ip.address/iipsrv/iipsrv.fcgi?IIIF=image1.tif/full/400,/0/default.jpg" />

<br/><img src="your.neighbours.ip.address/iipsrv/iipsrv.fcgi?IIIF=image1.tif/full/400,/0/default.jpg" />

Copy these links carefully – although you will of course have to to change the IP addresses in these URLs with the ones you came up with earlier.

Save the index.html file (hit Ctrl+o, then enter) and exit nano (hit Ctrl+x). Then enter your RPi’s IP address in your browser (on the laptop!) again, and look at the effect your changes had on your website.

8.2 IIIF Manifests

So wouldn’t it be nice if we could also embed additional metadata about each image (or even an entire collection of images) directly from the server of the image provider? In a computer readable, standardized format? This would allow us to give credit where credit is due. And if the original hosts have enriched their images in any way (with dates, transcriptions, annotations, etc.), we might want to reference those, or reuse them in our website in some way too. IIIF provides a solution for this: IIIF Manifests. This is a container file (in the JSON format), that contains metadata about an image collection as well as the IIIF compliant URLs to the contained images. For now, we will only look at a simple example, in detail we will learn about manifests and IIIF compliant image annotation later.

8.3 Download A Sample Manifest

Here again, you will need to follow your tutors’ instructions to download a sample manifest for your collection – or download the one we used in class here.

8.4. Adapt the Manifest to Your Needs

At this point, you don’t have to understand the file you’re reading in nano. Instead, just replace all instances of the string MY_IP_ADDRESS with your actual IP Address.

Warning: If you make a mistake here, your manifest will not work later. There should be 10 instances for you to replace. Go through slowly line by line.

When you are done double-checking, go through the file again, looking for the line "label": "Team [PI Name]", (line 9). Now, replace the string [PI NAME] by the name of your Raspberry Pi.]

Once you are done, save (hitting Ctrl+o, then enter) and exit nano (Ctrl+x).

8.5 Double Check that the File Loads

Check in the browser if your manifest is publically accessible. Open the laptop browser and enter:


The manifest JSON file should load in your browser.

9. Frankenstein

This is the point where we’ll really start taking advantage of the interoperability features IIIF has to offer, as we’ll aggregate, mix and match each other’s images. You’ll effectively be assembling your own “Monster” out of the Frankenstein manuscript images you and your peers are hosting. To do so, we’ll be working more with IIIF Manifests, so depending on your (and your peers’) technical background, this would be a good point for your tutors to introduce the basics of the JSON data format.

9.1 Loading Manifests in Mirador

On their laptop, that is also connected to our IIIFarm, your tutors will also has a version of Apache running, that should make data from his laptop accessible to you. In his web-shared folder he installed a version of Mirador, a IIIF compliant image viewer with a couple of nice features such as deep zooming. But one of this viewers coolest features is, that you can load IIIF manifests dynamically if you have the URL of the manifest. Luckily, each team knows the URL of their manifest, you have just entered it in your browser. Lets load them all – one by one – in Mirador.

Follow your tutor’s instructions to load their Mirador instance on your own IIIFarm-connected laptop and add your own manifest. Play with the viewer. Make yourself familiar with the functionality it provides. Then load some of the manifests of other teams into the viewer too.

9.2 Adding Metdata to the Manifest

At this point, your IIIF manifests do not yet provide a lot of metadata. Let’s first experiment by improving what is there already. Open your manifest (frankenstein.json) with nano and edit the existing JSON data.

Warning: Try not to change the file’s structure and don’t change the ids.

What you enter here does not have to make sense for now, but whenever you change something, save the file (hit CTRL+o, then enter – but don’t exit nano!) and reload your manifest in the Mirador viewer.

Try to see how mirador views the JSON data you changed. Which change is visualized where?

On the initial image folder we gave to you (/home/pi/Desktop/yourPisName/images/), your .png imagess still had individual names. Their name (a number) was the position of the image in the sequence of the entire notebook. Try to get this information into your canvas information. For example: if your files were called 023.png and 024.png, make sure that your canvas for image1.tif reads “Page 23”, and image2.tif would correspond to “Page 24”, etc.]

9.3. Including Other Teams’ Manifests

  1.  Study the canvas area of your manifest. Open another team’s manifest and look at their JSON. Find their canvases.
  2. Figure out if their two images should come before or after your own images.
  3. Try to copy their canvases from their manifest into your manifest: find the right position to add their canvases (either before or after your own canvases) copy them there.
  4. Load your manifest again in mirador. Did it work? If not… debug!
  5. Add the canvases of at least two other teams at the correct position in the sequence.

9.4 Finding the Missing Information Online

Maybe you would be happy to have more real metadata in your manifest. Luckily, the Shelley Godwin Archive recently published the entire notebook in IIIF. Here is the corresponding blog post:


Can you find the manifest? You cannot load it in our instance of Mirador (because we don’t have internet) but you can load the JSON manifest on your internet-connected laptop and use the given information to improve your own manifest to your heart’s content.

That’s it! You’ve now successfully installed a IIIF compliant image server on a RPi, shared images, and mixed, matched, and manipulated those of your peers. At this point, you should also be able to set up a similar environment for your own project on a Raspbian or Debian (Linux) system.

The summer school’s first edition was generously sponsored by the University of Antwerp’s Literature Department, the Antwerp Summer University, the Flemish Government’s Department of Economy, Science, and Innovation, Digital Humanities Flanders (DHu.F), and DARIAH-BE – the last of which provided us with the course’s hardware and the opportunity to develop these tutorials further. The development, teaching, and dissemination of this tutorial falls under the purview of the CLARIAH-VL project’s WP2 on Community Engagement, User Involvement and Training.

These tutorials are published under a Creative Commons Share Alike licence (CC-BY-SA 4.0). This means that you can re-use (share and adapt) these slides provided you provide sufficient attribution and publish and distribute the result under the same license.