Skip to content

Offline (Air-Gapped) Installation of MOJO

This guide covers installing and running the MOJO Platform on a Linux server that has no outbound internet access. Every artifact that the standard installer would download at runtime is instead prepared on an internet-connected staging machine and transferred to the target host via USB drive, SCP, or other secure media.

Metify Support Required

An offline installation requires coordination with Metify. Before you begin, contact support@metify.io to obtain:

  1. Your MOJO License Key
  2. Your encrypted license string (the MOJO_LICENSE value)
  3. Repository credentials (MOJO_REPOSITORY_USERNAME and MOJO_REPOSITORY_PASSWORD)

In a standard online installation these values are fetched automatically from the license server. In an offline installation you must receive them ahead of time and configure them manually.


Overview

A standard MOJO installation contacts the internet at four points:

Step What is downloaded Script
1 System packages (curl, jq, etc.) mojo-install
2 mojo-setup.tgz installer archive mojo-install
3 License + repository credentials from licenses.metify.io mojo-setup
4 Container images from download.metify.io mojo-launcher

An offline installation replaces each step with a local equivalent. The sections below walk through preparing and executing each one.


System Requirements

The target (air-gapped) host must meet the same requirements as an online installation:

Resource Requirement
OS Ubuntu 22.04/24.04, Debian 12/13, RHEL 9/10, Rocky Linux, Alma Linux
Memory Minimum 16 GB RAM
Disk Space At least 80 GB free (more if storing OS/firmware bundles)
Container Runtime Docker or Podman pre-installed

Container Runtime

The air-gapped host must already have Docker or Podman installed. The MOJO installer can install Docker automatically, but that process requires internet access. Install your container runtime from local packages or your organization's internal package repository before proceeding.


Phase 1 -- Prepare Artifacts (Internet-Connected Machine)

Perform the following steps on a staging machine that has internet access. All artifacts will be transferred to the air-gapped target later.

1.1 Download the Installer Archive

Download the mojo-setup.tgz archive and the mojo-install bootstrap script:

mkdir -p ~/mojo-offline && cd ~/mojo-offline

# Download the latest stable installer archive
curl -fLO https://download.metify.io/repository/filestore-external/mojo-installer/releases/latest/mojo-setup.tgz

# Download the bootstrap script (optional -- included in the archive)
curl -fLO https://download.metify.io/repository/filestore-external/mojo-installer/releases/latest/mojo-install

1.2 Download the System Dependency Packages

On the staging machine (or a machine running the same OS and version as the target), download the required packages for offline installation.

# Create a directory for the .deb packages
mkdir -p ~/mojo-offline/packages

# Download packages without installing
apt-get download curl jq sudo bash lsof util-linux \
    netcat-openbsd ldap-utils iproute2 ca-certificates tar gzip
mv *.deb ~/mojo-offline/packages/
mkdir -p ~/mojo-offline/packages

dnf download --resolve curl sudo bash jq lsof util-linux \
    nmap-ncat openldap-clients iproute ca-certificates tar gzip
mv *.rpm ~/mojo-offline/packages/

Note

Use --resolve (dnf) or download dependencies explicitly to ensure all transitive dependencies are included. The target machine may not have anything beyond a minimal OS installation.

1.3 Pull and Export Container Images

Log in to the MOJO repository and pull all required images. Then export them as tar archives.

# Authenticate with the MOJO repository
# Use the credentials provided by Metify support
docker login https://download.metify.io -u <MOJO_REPOSITORY_USERNAME>

# Pull all MOJO platform images
docker pull download.metify.io/mojo/coordinator:latest
docker pull download.metify.io/mojo/yoda-gui:latest
docker pull download.metify.io/mojo/postgres:16.3
docker pull download.metify.io/mojo/redis:7.2.4
docker pull download.metify.io/mojo/redfish-mock:latest
docker pull download.metify.io/mojo/consoler:1.0
docker pull download.metify.io/mojo/tor/grub-builder:1.0
docker pull download.metify.io/mojo/tor/ipxe-builder:c1834f-1.0
docker pull download.metify.io/mojo/tor/traefik:3.6.7
docker pull download.metify.io/mojo/tor/nginx:1.25.3
docker pull download.metify.io/mojo/tor/dhcpd:1.0
docker pull download.metify.io/mojo/tor/tftpd:1.0
docker pull download.metify.io/mojo/tor/pxe-helper:1.0

# Export all images into a single tar archive
docker save \
    download.metify.io/mojo/coordinator:latest \
    download.metify.io/mojo/yoda-gui:latest \
    download.metify.io/mojo/postgres:16.3 \
    download.metify.io/mojo/redis:7.2.4 \
    download.metify.io/mojo/redfish-mock:latest \
    download.metify.io/mojo/consoler:1.0 \
    download.metify.io/mojo/tor/grub-builder:1.0 \
    download.metify.io/mojo/tor/ipxe-builder:c1834f-1.0 \
    download.metify.io/mojo/tor/traefik:3.6.7 \
    download.metify.io/mojo/tor/nginx:1.25.3 \
    download.metify.io/mojo/tor/dhcpd:1.0 \
    download.metify.io/mojo/tor/tftpd:1.0 \
    download.metify.io/mojo/tor/pxe-helper:1.0 \
    -o ~/mojo-offline/mojo-images.tar

Compression

The image archive can be 5-10 GB. Compress it to speed up transfer:

gzip ~/mojo-offline/mojo-images.tar

Podman

If the staging machine uses Podman instead of Docker, replace docker with podman in the commands above. The exported tar format is compatible between Docker and Podman.

1.4 (Optional) Download OS and Firmware Bundles

If you plan to provision servers with MOJO, download the OS bundles and firmware bundles you need:

# OS Bundle example (Ubuntu 22.04)
curl -fL -o ~/mojo-offline/ubuntu-22.04.03-x86_64.tar.gz \
    --user "<MOJO_REPOSITORY_USERNAME>:<MOJO_REPOSITORY_PASSWORD>" \
    "https://download.metify.io/repository/osimages-external/images/ubuntu/22.04.03/x86_64/ubuntu-22.04.03-x86_64.tar.gz"

# MojoLive bundle (required for disk sanitization features)
curl -fL -o ~/mojo-offline/mojolive-1.0.0-x86_64.tar.gz \
    --user "<MOJO_REPOSITORY_USERNAME>:<MOJO_REPOSITORY_PASSWORD>" \
    "https://download.metify.io/repository/osimages-external/images/mojolive/1.0.0/x86_64/mojolive-1.0.0-x86_64.tar.gz"

1.5 Package Everything for Transfer

ls ~/mojo-offline/
# Expected contents:
#   mojo-install              (bootstrap script)
#   mojo-setup.tgz            (installer archive)
#   mojo-images.tar.gz        (container images)
#   packages/                 (system dependency packages)
#   ubuntu-22.04.03-x86_64.tar.gz   (optional OS bundle)
#   mojolive-1.0.0-x86_64.tar.gz    (optional MojoLive bundle)

Transfer the ~/mojo-offline/ directory to the air-gapped target machine using USB drive, SCP over a private network, or any approved file transfer method.


Phase 2 -- Install on the Air-Gapped Target

All steps below are performed on the target machine that has no internet access. Assume the offline artifacts have been placed at /tmp/mojo-offline/.

2.1 Install System Dependencies

sudo dpkg -i /tmp/mojo-offline/packages/*.deb
# If dependency errors occur:
sudo apt-get -f install    # only resolves from local cache
sudo rpm -ivh /tmp/mojo-offline/packages/*.rpm
# Or with dnf for automatic dependency resolution from local files:
sudo dnf localinstall /tmp/mojo-offline/packages/*.rpm

2.2 Extract the Installer

# Create the installation directory
sudo mkdir -p /opt/mojo

# Extract the setup archive
sudo tar xzf /tmp/mojo-offline/mojo-setup.tgz -C /opt/mojo

cd /opt/mojo

2.3 Load Container Images

Load the exported container images into the local container runtime:

# If compressed:
gunzip -c /tmp/mojo-offline/mojo-images.tar.gz | sudo docker load

# If uncompressed:
sudo docker load -i /tmp/mojo-offline/mojo-images.tar
gunzip -c /tmp/mojo-offline/mojo-images.tar.gz | sudo podman load

# Or uncompressed:
sudo podman load -i /tmp/mojo-offline/mojo-images.tar

After loading, tag each image with the local/mojo-* tags that MOJO expects. This mirrors what mojo-launcher does during an online pull_images:

# Set RUNTIME to your container command (docker or podman)
RUNTIME=docker   # or: RUNTIME=podman

sudo $RUNTIME tag download.metify.io/mojo/tor/traefik:3.6.7      local/mojo-proxy:latest
sudo $RUNTIME tag download.metify.io/mojo/tor/dhcpd:1.0           local/mojo-dhcpd:latest
sudo $RUNTIME tag download.metify.io/mojo/tor/tftpd:1.0           local/mojo-tftpd:latest
sudo $RUNTIME tag download.metify.io/mojo/tor/nginx:1.25.3        local/mojo-static-server:latest
sudo $RUNTIME tag download.metify.io/mojo/tor/pxe-helper:1.0      local/mojo-pxe-helper:latest
sudo $RUNTIME tag download.metify.io/mojo/tor/ipxe-builder:c1834f-1.0  local/mojo-ipxe-builder:latest
sudo $RUNTIME tag download.metify.io/mojo/tor/grub-builder:1.0    local/mojo-grub-builder:latest
sudo $RUNTIME tag download.metify.io/mojo/postgres:16.3           local/mojo-db:latest
sudo $RUNTIME tag download.metify.io/mojo/redis:7.2.4             local/mojo-redis:latest
sudo $RUNTIME tag download.metify.io/mojo/consoler:1.0            local/mojo-consoler:latest
sudo $RUNTIME tag download.metify.io/mojo/coordinator:latest      local/mojo-coordinator:latest
sudo $RUNTIME tag download.metify.io/mojo/yoda-gui:latest         local/mojo-gui:latest
sudo $RUNTIME tag download.metify.io/mojo/redfish-mock:latest     local/mojo-mock:latest

Verify the images are loaded:

sudo $RUNTIME images | grep "local/mojo"

You should see all 13 images tagged as local/mojo-*:latest.

2.4 Run Setup (Skip Connection Check)

Run mojo-setup with the --skip-connection-check flag since the host cannot reach external servers:

cd /opt/mojo
sudo ./mojo-setup --skip-connection-check

The setup wizard will prompt you for configuration values interactively. Key differences from an online install:

Telemetry

When prompted to disable telemetry, answer y (yes, disable):

Disable Anonymous Telemetry Data [yn]? [n]: y

Since the host has no internet access, telemetry data cannot be sent. Disabling it avoids silent curl timeouts during setup and launcher operations.

License Key and License Server

When prompted for your license key, enter the key provided by Metify support:

Mojo License Key? []: <your-license-key>

When prompted for the license server, keep the default. The setup will attempt to contact licenses.metify.io and fail, which is expected. You will see a warning:

Invalid license key: <your-key>
Could not pull a license from license server.

This warning is not fatal. The setup will continue. You will manually configure the license in the next step.

Network Configuration

Configure your network settings (interface, hostname, IP, netmask, gateway, DNS, DHCP) as you would in a standard installation. These settings apply to your local network and do not require internet access.

2.5 Configure License and Repository Credentials Manually

After mojo-setup completes, manually edit configs/mojo.env to add the values that would normally be fetched from the license server:

sudo vi /opt/mojo/configs/mojo.env

Set the following values (provided by Metify support):

MOJO_LICENSE_KEY="<your-license-key>"
MOJO_LICENSE="<your-encrypted-license-string>"
MOJO_REPOSITORY_URL="https://download.metify.io"
MOJO_REPOSITORY_USERNAME="<your-repo-username>"
MOJO_REPOSITORY_PASSWORD="<your-repo-password>"
MOJO_TELEMETRY_DISABLE="true"

MOJO_LICENSE vs MOJO_LICENSE_KEY

These are two different values. MOJO_LICENSE_KEY is the API key used to authenticate with the license server. MOJO_LICENSE is the actual encrypted license string that the platform validates at runtime. Both must be set for offline installations. Contact support@metify.io to obtain your MOJO_LICENSE value.

2.6 Start MOJO (Without Pulling Images)

Since the images are already loaded locally, start MOJO without the --update flag:

cd /opt/mojo
sudo ./mojo-launcher start mojo

Do Not Use --update

The --update flag triggers pull_images() which attempts to docker login and pull images from download.metify.io. On an air-gapped host this will fail. Always use start mojo without --update for offline installations.

The launcher will:

  1. Verify the license agreement
  2. Check container runtime prerequisites
  3. Render configuration templates
  4. Start all MOJO services via Docker/Podman Compose

Once complete, you should see:

*********************************************
Mojo Platform started successfully!
https://<your-configured-hostname>
*********************************************

2.7 Access the Web Console

Open a browser and navigate to:

https://<your-configured-hostname>

Default login credentials:

  • Username: admin
  • Password: admin

Change Default Password

Change the default password immediately after your first login.


Phase 3 -- Load OS and Firmware Bundles (Optional)

If you downloaded OS bundles or firmware bundles during Phase 1, load them now. MOJO must be running before loading bundles.

Load an OS Bundle from a Local File

cd /opt/mojo
sudo ./mojo-manage --os-image

When prompted Do you have a local os bundle file?, answer y and provide the path:

Do you have a local os bundle file? [yn]: y
Filepath? []: /tmp/mojo-offline/ubuntu-22.04.03-x86_64.tar.gz

Load MojoLive

sudo ./mojo-manage --os-image
Do you have a local os bundle file? [yn]: y
Filepath? []: /tmp/mojo-offline/mojolive-1.0.0-x86_64.tar.gz

Load a Firmware Bundle from a Local File

sudo ./mojo-manage --firmware

When prompted Do you have a local firmware bundle file?, answer y and provide the path.


Updating an Offline Installation

To update MOJO on an air-gapped host, repeat the artifact preparation process on an internet-connected staging machine:

  1. Download the new mojo-setup.tgz from download.metify.io
  2. Pull the latest container images and export them with docker save
  3. Transfer both to the air-gapped host

Then on the target:

# Stop MOJO
cd /opt/mojo
sudo ./mojo-launcher stop mojo

# Extract the updated installer (preserves configs/ and volumes/)
sudo tar xzf /tmp/mojo-offline/mojo-setup.tgz -C /opt/mojo

# Load updated container images
gunzip -c /tmp/mojo-offline/mojo-images.tar.gz | sudo docker load

# Re-tag all images (same commands as Phase 2, Step 2.3)
# ...

# Re-run setup to pick up any new configuration options
sudo ./mojo-setup --skip-connection-check

# Start MOJO
sudo ./mojo-launcher start mojo

Configuration Preservation

The mojo-setup command detects an existing configs/mojo.env and enters reconfiguration mode. It backs up your current configuration before applying changes, so your network settings, license, and other customizations are preserved.


Troubleshooting

"Cannot connect to the docker engine"

Ensure Docker or Podman is installed and running before starting MOJO:

sudo systemctl status docker    # or: sudo systemctl status podman

Images not found or services fail to start

Verify that all local tags exist:

sudo docker images | grep "local/mojo"

If any are missing, re-run the docker load and docker tag commands from Phase 2, Step 2.3.

License errors at runtime

Confirm that both MOJO_LICENSE_KEY and MOJO_LICENSE are set in configs/mojo.env. The MOJO_LICENSE value is the encrypted license string, not the API key. Contact support@metify.io if you need this value.

Telemetry timeout warnings

If you see slow startup or timeout messages related to telemetry, verify that MOJO_TELEMETRY_DISABLE="true" is set in configs/mojo.env.


Support

If you have any questions about offline installations, please contact us at support@metify.io.