Installing MMMF on a Proxmox container running Ubuntu Server works well if you want a lightweight, self-hosted money market fund forecasting app without spinning up a full VM. MMMF (Max Money Market Funds) ships as a Docker image, so the cleanest approach is running Docker inside an Ubuntu LXC container and keeping the app’s JSON data on a persistent bind mount.
This guide covers the full setup: preparing the Proxmox LXC container, installing Docker on Ubuntu, deploying MMMF with Docker Compose, preserving data, updating the app, and optionally putting it behind Caddy for a cleaner local or private domain.

Quick Answer: To install MMMF on a Proxmox Ubuntu container, create an Ubuntu LXC with nesting enabled, install Docker Engine inside the container, create a persistent data folder, then run the official MMMF Docker image with Docker Compose. MMMF listens on port 5173 and stores persistent data under /app/data inside the container.
What You Are Installing
MMMF is a self-hosted financial forecasting app designed to predict future account balances and help estimate how much money can safely sit in high-yield or money market accounts. According to the official MMMF repository, the app supports balance forecasting, future-dated transactions, recurring transactions, lowest-balance tracking, file-based JSON persistence, currency/date formatting, and multiple interface languages.
The project provides a ready-made Docker image:
jasonyangee/mmmf:latest
It also provides a GitHub Container Registry image:
ghcr.io/jasonyang-ee/mmmf:latest
For a Proxmox homelab, the Docker image is the practical option. You don’t need to build the app manually unless you want to modify the source code.
Recommended Setup for Proxmox
Let’s be honest about Docker inside LXC. Proxmox containers are lightweight and efficient, but Docker inside an LXC container is a nested container setup. Proxmox’s own pct container documentation notes that nesting containers inside a Proxmox QEMU VM remains the recommended approach when you want stronger isolation.
That doesn’t mean Docker in LXC is unusable. For a personal homelab app like MMMF, an Ubuntu LXC with nesting enabled is commonly used and works well when configured carefully. If you’re exposing this to the public internet, handling sensitive financial data for multiple people, or running untrusted workloads, use a small Ubuntu VM instead.
| Component | Recommended Value | Why It Matters |
|---|---|---|
| Container OS | Ubuntu 24.04 LTS or 22.04 LTS | Both are supported by Docker Engine and work well in Proxmox LXC. |
| CPU | 1–2 cores | MMMF is lightweight and doesn’t need heavy compute resources. |
| RAM | 512 MB minimum, 1 GB preferred | Enough for Ubuntu, Docker, and one small web app container. |
| Disk | 4–8 GB minimum | Leaves room for Docker image layers, logs, and app data. |
| Network | Static DHCP lease or static IP | Makes it easier to access MMMF at a stable address. |
| LXC Features | nesting=1, optionally keyctl=1 |
Required for running Docker reliably inside the container. |
If you already run Docker workloads on Proxmox, this setup will feel familiar. I’d keep MMMF in its own small container rather than mixing it into a crowded Docker host, mainly because financial planning data is easier to back up and reason about when the service is isolated. If you’re already maintaining several Docker services, the same operational habits from Proxmox Docker networking apply here too: keep ports intentional, store data outside the container image, and document the service path.
Step 1: Create the Ubuntu LXC Container
You can create the container from the Proxmox web UI or from the Proxmox host shell. The UI is easier if you prefer a visual workflow:
- Open Proxmox: Go to your Proxmox web interface and select the node where you want to run MMMF.
- Create CT: Click Create CT.
- Choose hostname: Use something clear, such as
mmmf. - Select template: Choose an Ubuntu 24.04 or Ubuntu 22.04 LXC template.
- Assign storage: 4 GB works, but 8 GB gives more breathing room for Docker layers.
- Assign CPU and memory: Use 1–2 CPU cores and 1024 MB RAM.
- Configure network: Use DHCP with a router-side reservation, or assign a static IP directly.
- Finish creation: Don’t start the container yet if you still need to enable nesting.
After creating the container, enable nesting from the Proxmox host shell. Replace 120 with your actual container ID.
pct set 120 -features nesting=1,keyctl=1
Start the container:
pct start 120
Then enter it from the Proxmox host:
pct enter 120
Note: If Docker fails later with storage-driver or overlay errors, the issue is usually related to LXC nesting, storage backend behavior, or running Docker in an unprivileged container. For a production-grade Docker host, a small VM is still the cleaner option.
Step 2: Update Ubuntu Inside the Container
Inside the Ubuntu container, update the package index and install basic tools:
apt update
apt upgrade -y
apt install -y ca-certificates curl gnupg nano
Check the Ubuntu version:
lsb_release -a
You should see Ubuntu 22.04 LTS or Ubuntu 24.04 LTS. Docker’s Ubuntu install guide currently lists Ubuntu 22.04 LTS and 24.04 LTS among supported versions, along with newer Ubuntu releases. If you’re using an older LXC template, update the template before continuing.
Step 3: Install Docker Engine on Ubuntu
Use Docker’s official apt repository rather than Ubuntu’s older docker.io package. First, remove conflicting packages if they exist:
for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do
apt remove -y "$pkg" 2>/dev/null || true
done
Add Docker’s official GPG key:
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
-o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
Add the Docker repository:
tee /etc/apt/sources.list.d/docker.sources <<EOF
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}")
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF
Install Docker Engine and the Compose plugin:
apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Enable and start Docker:
systemctl enable docker
systemctl start docker
Verify the installation:
docker version
docker compose version
docker run hello-world
If hello-world prints a success message, Docker is working inside the Proxmox container.
Step 4: Create a Clean Directory for MMMF
Keep self-hosted services under a predictable path. I prefer /opt/stacks for Docker Compose apps inside a small LXC container:
mkdir -p /opt/stacks/mmmf/mmmf/data
cd /opt/stacks/mmmf
The slightly repeated path is intentional. The official Compose example mounts ./mmmf/data to /app/data. Keeping that structure makes it easy to compare your deployment against the upstream example later.
Set safe ownership on the folder:
chown -R root:root /opt/stacks/mmmf
chmod -R 755 /opt/stacks/mmmf
MMMF uses file-based JSON persistence, so this folder matters. If you delete it, you delete the app’s saved data. This is the part you should include in your backup routine.
Step 5: Create the MMMF Docker Compose File
Create the Compose file:
nano /opt/stacks/mmmf/docker-compose.yml
Paste this configuration:
services:
mmmf:
image: jasonyangee/mmmf:latest
container_name: mmmf
restart: unless-stopped
ports:
- "5173:5173"
volumes:
- ./mmmf/data:/app/data
environment:
TZ: Asia/Kolkata
DEFAULT_LANGUAGE: en
Save and exit the editor.
The important parts are:
image: Pulls the official MMMF image from Docker Hub.restart: unless-stopped: Restarts MMMF after container crashes or host reboots unless you manually stop it.ports: Publishes MMMF on port5173.volumes: Preserves data under/opt/stacks/mmmf/mmmf/dataon the Ubuntu container.TZ: Sets the timezone. Change this if your server should use another timezone.DEFAULT_LANGUAGE: Uses English by default. The upstream project listsen,zht,ja, andesas supported values.
The Compose Specification is designed for defining services, networks, volumes, and related runtime configuration in a single file, which is why it’s a better long-term choice than a one-off docker run command. You can compare the structure with Docker’s Compose file reference if you want to expand this stack later.
Step 6: Start MMMF
From the MMMF stack directory, pull the image and start the container:
cd /opt/stacks/mmmf
docker compose pull
docker compose up -d
Check whether the container is running:
docker ps
You should see a container named mmmf with port 5173 published.
Check the logs:
docker logs -f mmmf
If there are no repeated crash messages, open MMMF in your browser:
http://YOUR_CONTAINER_IP:5173
For example:
http://192.168.1.120:5173
If you see the MMMF interface, the installation is working.
Step 7: Restrict Port Exposure if Needed
The basic Compose file publishes MMMF on all interfaces inside the Ubuntu container. That’s fine on a trusted LAN, but not ideal if the container is reachable from wider networks.
If you plan to put MMMF behind Caddy, Nginx, Traefik, or a VPN-only reverse proxy, bind the app to localhost instead:
ports:
- "127.0.0.1:5173:5173"
Then recreate the container:
cd /opt/stacks/mmmf
docker compose up -d
This prevents direct LAN access to port 5173 from other machines and allows only local reverse proxy access. This is especially important because Docker-published ports can interact with host firewall rules in surprising ways. Docker’s firewall behavior documentation explains that published container ports may bypass typical UFW expectations because Docker uses its own packet filtering and NAT rules.
If you’ve already run into Docker networking surprises on Proxmox, the troubleshooting approach in Docker DNS failures is also useful here: test from the host, test from inside the container, and don’t assume the firewall path is the same for every interface.
Optional: Put MMMF Behind Caddy
If you already run Caddy as your internal reverse proxy, you can give MMMF a cleaner hostname such as:
mmmf.home.arpa
mmmf.lan
mmmf.yourdomain.com
For a Caddy instance running on the same Ubuntu container, a simple Caddyfile looks like this:
mmmf.home.arpa {
reverse_proxy 127.0.0.1:5173
}
If Caddy runs on another host, point it to the MMMF container IP:
mmmf.home.arpa {
reverse_proxy 192.168.1.120:5173
}
Caddy’s reverse proxy quickstart is a useful reference if you want to add HTTPS, private DNS, or a more complex upstream layout.
For a Tailscale-only private setup, you can also combine this with the same pattern used in Caddy Tailscale certificates. That keeps MMMF off the public internet while still giving you a valid HTTPS endpoint across your tailnet.
How to Update MMMF Later
MMMF currently tracks releases through its GitHub repository, and the Docker image can be updated by pulling the latest image and recreating the container.
cd /opt/stacks/mmmf
docker compose pull
docker compose up -d
Clean up unused old image layers after confirming the app works:
docker image prune -f
Check the running image:
docker inspect mmmf --format '{{.Config.Image}}'
If you prefer predictable upgrades, pin a specific image tag instead of using latest. For example, if the project publishes a versioned tag you trust, change the image line like this:
image: jasonyangee/mmmf:v1.1.4
Then run:
docker compose pull
docker compose up -d
Pro Tip: For small financial apps, avoid blind auto-updates unless you have backups. Pulling the latest image manually gives you a chance to check release notes and confirm the app still reads your existing data correctly.
How to Back Up MMMF Data
Because MMMF stores persistent data in /app/data inside the container, your real backup target is this host-side folder:
/opt/stacks/mmmf/mmmf/data
Stop the container before taking a clean manual backup:
cd /opt/stacks/mmmf
docker compose stop
tar -czf /root/mmmf-backup-$(date +%F).tar.gz -C /opt/stacks/mmmf mmmf/data
docker compose start
To restore later, stop MMMF, extract the backup over the data directory, and start the container again:
cd /opt/stacks/mmmf
docker compose stop
tar -xzf /root/mmmf-backup-YYYY-MM-DD.tar.gz -C /opt/stacks/mmmf
docker compose start
If you use Proxmox Backup Server or scheduled LXC backups, still keep a separate copy of the MMMF data folder. Full-container backups are useful, but app-level backups are faster to inspect and restore.
Troubleshooting Common MMMF Installation Problems
Docker Doesn’t Start Inside the LXC Container
First, confirm nesting is enabled on the Proxmox host:
pct config 120 | grep features
You should see something like:
features: keyctl=1,nesting=1
If not, stop the container and enable the features:
pct stop 120
pct set 120 -features nesting=1,keyctl=1
pct start 120
Then enter the container and restart Docker:
systemctl restart docker
systemctl status docker
MMMF Starts but the Browser Can’t Reach It
Check whether the container is running:
docker ps
Check whether Ubuntu is listening on port 5173:
ss -tulpn | grep 5173
From another machine on your LAN, test the port:
curl -I http://YOUR_CONTAINER_IP:5173
If you used the localhost-only binding:
127.0.0.1:5173:5173
then MMMF won’t be reachable directly from another machine. That’s expected. You need to access it through the reverse proxy running on the same host, or change the port binding back to:
5173:5173
MMMF Data Disappears After Recreating the Container
This usually means the volume path was wrong or the container was originally started without a bind mount. Confirm your Compose file includes:
volumes:
- ./mmmf/data:/app/data
Then check the folder:
ls -la /opt/stacks/mmmf/mmmf/data
If the directory is empty, restore from backup if available. Docker containers are disposable; the bind-mounted data folder is the part that must survive.
Docker Pull Fails With DNS Errors
Check DNS resolution inside the Ubuntu LXC:
getent hosts registry-1.docker.io
getent hosts github.com
If DNS fails inside the LXC but works on the Proxmox host, check the container’s DNS settings in Proxmox:
pct config 120 | grep -i nameserver
You can set DNS from the Proxmox host:
pct set 120 -nameserver 1.1.1.1
Or use your local resolver, Pi-hole, or Technitium DNS server if your homelab depends on internal DNS records.
Security Notes Before You Use MMMF Seriously
MMMF is useful for personal forecasting, but you should still treat it as a private financial tool. Don’t expose it directly to the public internet without authentication, HTTPS, and a clear access-control layer.
- Keep it private: Prefer LAN-only, VPN-only, or Tailscale-only access.
- Use HTTPS: Put it behind Caddy, Traefik, Nginx Proxy Manager, or another reverse proxy if accessing it across devices.
- Back up the data folder: The JSON data is the important part of the deployment.
- Avoid shared public instances: If multiple people use it, understand how the app stores and separates data before relying on it.
- Review updates manually: Financial planning data isn’t the place for careless upgrades.
If your homelab already has a reverse proxy and private DNS pattern, MMMF fits neatly into it. The same ideas from DNS service discovery can be reused here: give the service a stable internal name, point it to the right host, and let Caddy or your proxy handle the friendly endpoint.
Useful MMMF Management Commands
| Task | Command |
|---|---|
| Start MMMF | docker compose up -d |
| Stop MMMF | docker compose stop |
| Restart MMMF | docker compose restart |
| View logs | docker logs -f mmmf |
| Update image | docker compose pull && docker compose up -d |
| Check container status | docker ps |
| Check listening port | ss -tulpn | grep 5173 |
| Enter Proxmox container | pct enter 120 |
Final Thoughts
Installing MMMF on a Proxmox container running Ubuntu Server is a clean weekend homelab project: small resource footprint, simple Docker Compose deployment, and easy backup requirements. The key is treating the LXC container as the host, Docker as the app runtime, and /opt/stacks/mmmf/mmmf/data as the part that must be protected.
For a private LAN or VPN-only setup, this approach works well. For anything public-facing or multi-user, move the same Docker Compose stack into a small Ubuntu VM, put it behind a proper reverse proxy, and add an authentication layer before trusting it with real financial planning data.