Web Terminals and X11 Remote Desktop Access for Personal Virtual Applications

One of the components I’ve found useful when putting together virtual machines / Docker containers is a simple web based terminal that gives me terminal access into the machine.

This is a living post where I can keep notes on the various solutions I’ve tried…

Jupyter Notebooks

If you run a Jupyter notebook server, or JupyterLab, in a container, you get a web based terminal for free, albeit running under whatever user/privileges the Jupyter user environment is using.


Browser based terminal; requires a node install, which is a faff, but it seems to work quite well…  Here’s a Dockerfile fragment:

#Install node.js
RUN apt-get update && apt-get install -y build-essential
RUN curl -sL https://deb.nodesource.com/setup_8.x | bash && \
apt-get install -y nodejs && apt-get clean
RUN npm i npm@latest -g

#Install web terminal - gritty
#Provides access into container via in-browser terminal
RUN npm i gritty -g --unsafe-perm

#Use the terminal as the default running service for the container
#When the container is running, we should be able to access browser terminal into it
CMD gritty

The --no-perms thing for me was required to get it to install cleanly…

You’d probably want to run this alongside another process. See Docker docs – Run multiple services in a container for now. I need to work on my own reusable recipe to support this general sort of activity. (The supervisord route is probable the best way..? Example (untested), untested simple example – use /usr/bin/supervisord as the CMD , untested tutorial)

Here’s another example in a simple Vagrantfile:

Vagrant.configure("2") do |config|

#------------------------- PROVIDER: VIRTUALBOX (BUILD) ------------------------------

    config.vm.provider :virtualbox do |virtualbox|

        #Stick with the default key

        #For local testing:
        config.vm.box = "ubuntu/xenial64"

        config.vm.hostname = "gritty-test"

        virtualbox.name = "gritty_test"
        virtualbox.memory = 1024
        virtualbox.cpus = 1
        # virtualbox.gui = true

        #Don't bother updating guest additions
        config.vbguest.auto_update = false

        #---- START PORT FORWARDING ----
        #Registered ports: https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers

        config.vm.network :forwarded_port, guest: 1337, host: 8898, auto_correct: true

        #---- END PORT FORWARDING ----

#------------------------- END PROVIDER: VIRTUALBOX (BUILD) ------------------------------

  config.vm.provision :shell, :inline => <<-SH

    if [ ! -f /vagrant/.firstrun_setup.done ]; then
        apt-get update && apt-get install -y build-essential
        curl -sL https://deb.nodesource.com/setup_8.x | bash && apt-get install -y nodejs && apt-get clean
        npm i npm@latest -g

        #Install web terminal - gritty
        #Provides access into container via in-browser terminal
        npm i gritty -g --unsafe-perm

        cp /vagrant/gritty.service /lib/systemd/system/gritty.service

    	# Enable autostart
	systemctl enable gritty.service

	# Refresh service config
	systemctl daemon-reload

	#(Re)start service
	systemctl restart gritty.service

        touch /vagrant/.firstrun_setup.done

   #config.vm.provision "shell", inline: "gritty --port 1337 &", run: "always"


And here's an example gritty.service file:

Description=OU Gritty Terminal Demo

#When to bring the service up
#via https://www.freedesktop.org/wiki/Software/systemd/NetworkTarget/
#Wait for a network stack to appear
#If we actually need the network to have a routable IP address:


#ExecStartPre=mkdir -p /vagrant/openrefine_projects

ExecStart=/usr/bin/gritty --port ${GRITTYPORT}


Alternatives: xterm.js seems to provide the basis of several terminal servers such as ttyd; here’s an example with authentication from Duke: mccahill/xterm-js-docker; and a minimal example server: simon-engledew/dockerweb. Here’s an example of a web ssh terminal with a ore complete config file example here.


Another route to a web based terminal via the browser is to cheat – and allow access to VM desktop through the browser. This can be done with using a virtualised desktop. For example, novnc eg https://github.com/psharkey/docker/tree/master/novnc with other containers to compose with it.

The dit4c/dockerfile-dit4c-container-x11 container is a nice example of a set of Docker container layers to support X11 exposure via a browser using novnc.

The new Docker Desktop looks like it will have some GUI tools to help assemble applications in Docker Desktop so it’d be great if that included a robust, templated containers to handle both X11, and terminal access, through a browser.

See also: Apache Guacamole; example – Guacamole and Wine – Running Small Legacy Windows Apps Via a Browser or Accessing GUI Apps (Audacity) Via a Browser from a Container Using Guacamole.

PS here’s a set-up for running under supervisord in a container.

Docker – multiple services in same container recipe

This uses a docker-compose file (run using docker-compose up) that uses a local Dockerfile that calls on build.sh script to create a container image that runs a couple of services as specified in the supervisord.conf file. Note the requirement on installing the supervisor package.

First, the supervisord.conf script; this is responsible for starting the running applications (and replaces any services; which means my build files need to separate our services and copy them over / start them up if we are not doing the docker build).


command = /usr/bin/gritty --port 80

command = /usr/bin/gritty --port 8899

The build.sh script is responsible for installing the necessary packages into the container.

apt-get update && apt-get install -y build-essential curl
curl -sL https://deb.nodesource.com/setup_8.x | bash && apt-get install -y nodejs && apt-get clean
npm i npm@latest -g

#Install web terminal - gritty
#Provides access into container via in-browser terminal
npm i gritty -g --unsafe-perm

The Dockerfile orchestrates the build of the container.

FROM ubuntu:16.04

RUN apt-get update && apt-get install -y supervisor

ADD build.sh /root/build.sh
RUN chmod +x /root/build.sh && /root/build.sh

RUN apt-get clean

COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf

CMD ["/usr/bin/supervisord"]

The docker-compose.yaml orchestrates the deployment of the container, including port mappings.

version: '3'
    build: .
     - "8087:80"
     - "8097:8899"

Running the container should publish terminals running as separate services on different ports (host ports 8087 and 8097.

One comment

  1. olberger

    I guess Guacamole its really interesting here, even if I haven’t implemented it myself to really understand all pros and cons

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.