Running emoncms in a Linux container

When I was renovating the house I currently live in, I picked up an EmonTx v3 and some clip-on current sensors from so that I could monitor the house power consumption more accurately than just getting a bill every 2 months from my energy supplier (even with a smart meter installed, they only provide monthly graphs). A few years down the road, I’m getting to the point of installing it.

The Tx only provides monitoring capabilities, and I had intended to pick up the Pi half of the device later on (it was, and is, a few hundred Euro), or otherwise build my own Pi receiver by adding a 433MHz antenna board. Roll on 2022, and I’ve decided instead to add the ESP8266 WiFi board option to the Tx for about 20 euro, and I’ll host the Emoncms functionality as a Linux container alongside my other services.


  • The VPS host is already set up to do containerised hosting
  • Offsite backups at the host level
  • See what power consumption is like while I’m not at home


  • Router down => no logging of data
  • Router down => no rendering on a local screen either
  • Net glitches ==> ditto both

If I change my mind, it’ll be easy to bring the data across from the VPS, and dig a Pi out of my boxes of bits to run locally. It does mean for holidays that I’ll probably want to leave the router on as well as the fridge so that the accounting is accurate.

Non-Pi installation

The installation instructions (archive) require that the person doing the installation edit a config file. The installer then kicks off a bunch of apt-get commands to grab packages from the Debian/Ubuntu repositories. With my containerised hosting, I’m trying to avoid manually doing anything – I want to be able to just create the container, and have cloud-init do the relevant setup work (yes, this is what Puppet/Chef are for, but I’m trying to avoid doing a complete orchestration layer for my personal server).

Emoncms will not install on Ubuntu 22.04, because various packages like php are too new; as of the date of this post, Emoncms requires php-7.4 or lower. The Mosquitto client also fails to build cleanly on Ubuntu 22.04. Ubuntu 20.04 is fine though, and easy enough to do with a LXD container.

I ended up with this shell script, based on the project’s

cd /opt/openenergymonitor
git clone
cd EmonScripts
git checkout stable
cd install
cp emonsd.config.ini config.ini
sed -i\
    -e 's/hostname=emonpi/hostname=emoncms/'\
    -e 's/emonSD_pi_env=1/emonSD_pi_env=0/'\
    -e 's/install_emonhub=true/install_emonhub=false/'\
    -e 's/install_emoncms_emonpi_modules=true/install_emoncms_emonpi_modules=false/'\
    -e 's/install_firmware=true/install_firmware=false/'\
    -e 's/install_emonpilcd=true/install_emonpilcd=false/'\
    -e 's/install_emonsd=true/install_emonsd=false/'\
    -e 's/install_wifiap=true/install_wifiap=false/'\
    -e 's/emoncms_emonpi_modules\[/#emoncms_emonpi_modules[/'\
yes n | ./

Because the demandshaper part of the project hard-codes the installation username, I decided it was easier to tell cloud-init to create a username called ‘pi’, and I’ll call the script above as the pi user. I can also preload all the packages that the project shell scripts try to install; this will obviously drift as they change requirements, but as I don’t intend to be deploying emoncms daily it’s easy to just update the YAML config when needed.

The YAML profile for LXD looks like

  boot.autostart.priority: "10"
  cloud-init.user-data: |
    package_upgrade: true
    package_reboot_if_required: true
    locale: en_IE.UTF-8
      - apache2
      - build-essential
      - curl
      - gettext
      - git
      - gpg
      - libmosquitto-dev
      - mariadb-client
      - mariadb-server
      - mosquitto
      - php
      - php-curl
      - php-dev
      - php-gd
      - php-pear
      - php-mysql
      - php-mbstring
      - python3
      - python3-pip
      - redis-server
    - path: /usr/local/bin/git-setup
      permissions: '0755'
      encoding: b64
      content: |
    - path: /etc/apt/apt.conf.d/02-proxy.conf
      permissions: '0644'
      encoding: b64
      content: |
      - pi
      - default
      - name: pi
        primary_group: pi
        groups: sudo
        shell: /bin/bash
        sudo: "ALL=(ALL) NOPASSWD: ALL"
      - [ /usr/local/bin/git-setup, emoncms ]
      - [ /usr/local/bin/git-copier, emoncms ]
      - [ sudo, -u, pi, /usr/local/bin/emoncmssetup ]
      - [ /usr/local/bin/disable-ua ] |
    version: 2
        dhcp-identifier: mac
        dhcp4: true
description: emoncms service
    name: eth0
    network: lxdbr0
    type: nic
    path: /
    pool: default
    type: disk
    size: 3GB
name: emoncms

I’m letting Caddy handle the web service requirements by acting as a reverse proxy to the Apache2 instance that runs in the emoncms container. The reverse proxy configuration is trivial, and accomplished with one instruction. The explicit http and https bits are to ensure that the software on the 8266 can actually submit the data without Caddy doing an automatic redirect of port 80 to port 443; the 8266 supports https, but only if you provide the SHA of the certificate. Given I’m using LetsEncrypt, I don’t want to be reconfiguring it every 90 days.

http://somehost https://somehost {
        reverse_proxy emoncms.lxd

Submitting data

Getting data to submit is pretty easy.

  • Log in to emoncms as normal and set it up (just follow the official docs)
  • Add a device, ignore the device key
  • Power down the emonTx
  • Connect the ESP8266 module
  • Power up the emonTx
  • Connect to the new AP
  • Change over to using the local Wi-Fi
  • Connect to the ESP8266 again on the new local IP from the house Wi-Fi (ask the router what the IP is)
  • Configure the emoncms section with hostname, device name and API key (Setup, My Account, API R/W key)
  • Save

The ESP8266 only does IPv4, so don’t use IPv6-only hosting.