jahed.dev

Run MEGAcmd CLI in a Docker container

I've been looking into cloud storage for a small scheduled backup. MEGA caught my eye since it has end-to-end encryption, a generous 20GB to 30GB free tier, and no bandwidth charges or limits; though, I'm sure there's some throttling involved. It also has a command-line interface called MEGAcmd for scripting (also known as MEGA CMD).

MEGA isn't perfect, as seen on Rclone's overview table, its APIs lack a lot of features. But it's well-known and well reviewed so I'm willing to give it a go.

For this backup, I didn't use Restic and Rclone as their integration uses a reverse engineered library which doesn't support a bunch of basic features which Rclone relies on. So if the official MEGAcmd CLI is good enough, I can avoid introducing additional software and compatibility issues into the chain.

One of the downsides of MEGAcmd is that it's not distributed as a standalone CLI on Linux. It needs to be installed with a package manager and relies on a server running in the background. For scheduled backups, I'd rather not have that server always running and hogging resources. I'd also rather not install third-party packages. The solution? Containers.

Existing solutions

MEGAcmd does not distribute official container images, but they do provide Dockerfiles in their code repository which build from source. However, they seem to be strictly for building MEGAcmd, not running it in a container.

There are many other attempts at containerising MEGAcmd, but they differ and often involve build steps I'd rather avoid to keep things simple. The aim is to use a well maintained official base image and an official MEGAcmd release. Nothing inbetween. Updates should also be straight forward without relying on a third-party.

Create a new user

We'll be using Podman instead of Docker as it's a lot lighter and can run rootless. Additionally, to avoid accidentally giving MEGAcmd read or write permissions to files it doesn't need access to, we can create a new user.

sudo adduser mega-cmd
su - mega-cmd

Build a container image

We'll need a Dockerfile:

# ~/docker-megacmd/Dockerfile
FROM ubuntu:noble

WORKDIR /root

RUN apt-get update && apt-get install -y curl \
    && curl -O https://mega.nz/linux/repo/xUbuntu_24.04/amd64/megacmd-xUbuntu_24.04_amd64.deb \
    && apt-get install -y "$PWD/megacmd-xUbuntu_24.04_amd64.deb" \
    && rm "$PWD/megacmd-xUbuntu_24.04_amd64.deb" \
    && rm -rf /var/lib/apt/lists/*

CMD ["mega-cmd-server"]

MEGAcmd doesn't officially support Alpine so I ended up using Ubuntu. Using official pre-built packages involves a lot less maintenance than manually installing dependencies to build and run MEGAcmd in an unsupported environment.

We can build the image with Podman.

podman build -t megacmd ~/docker-megacmd

Sometimes Podman will give bus-related warnings. Configuring cgroup_manager fixes that. This can be done with a command line flag, but using a configuration file ensures all commands we run uses the same configuration. Otherwise, it can cause incompatibility issues.

# ~/.config/containers/containers.conf.d/containers.conf
[engine]
cgroup_manager="cgroupfs"

Start a container

Once the image is built, we can create and start a container.

podman create --name "megacmd" --restart "on-failure:3" \
  -v megacmd-data:/root/.megaCmd:rw \
  -v /etc/machine-id:/etc/machine-id:ro \
  -v /myfiles:/myfiles:ro \
  megacmd
podman start megacmd

Since mega-cmd-server is the default CMD, starting the container will start the server and tie them together. If the server stops, the container will stop too. The server will also log everything that its doing which Podman will automatically record. We can view and query the logs with:

podman logs megacmd

Stop a container

Annoyingly, mega-cmd-server does not handle signals. This includes the default SIGTERM sent by podman stop. So Podman will timeout after the default 10 seconds and force kill the process. To safely stop the server, we need to use mega-quit. This means we should avoid using --restart=always as it conflicts with mega-quit. We can follow up with podman stop just in case.

podman exec megacmd mega-quit
podman stop megacmd

We could write a small wrapper script to catch signals and call mega-quit, but that will introduce more maintenance work.

Run commands

MEGAcmd provides mega-cmd which is an interactive shell to run various commands. However, with this container approach, it's better to run those commands standalone with podman exec. They're all available using a mega- prefix. So whoami is mega-whoami.

podman exec megacmd mega-whoami

Most of these mega-<cmd> commands are simple wrappers around mega-exec <cmd> so we could run that directly.

We can run one-off commands in separate throwaway containers too.

podman run --rm \
  -v megacmd-data:/root/.megaCmd:rw \
  -v /etc/machine-id:/etc/machine-id:ro \
  -v /myfiles:/myfiles:ro \
  megacmd mega-whoami

MEGAcmd commands always start mega-cmd-server if it's not already running, so we're better off using podman start and podman exec to avoid unnecessary resource usage.

Update container images

To update MEGAcmd and the Ubuntu base image, we can rebuild with --no-caches.

podman build -t megacmd --no-caches ~/docker-megacmd

After that's done, we'll need to remove and recreate any existing containers.

Since MEGAcmd on Linux relies on the distro's package manager, mega-update is not available to automatically update MEGAcmd. You can technically update the container using the usual apt commands, but that introduces critical but temporary state into the container which is best avoided. You can schedule a cronjob to rebuild the container image instead.

Conclusion

That's pretty much everything. MEGAcmd now runs in a container and can easily be started, stopped and removed. To switch MEGA users or configuration, we can change the named volume. There's a lot we can do more easily with MEGAcmd in a container.

Thanks for reading.