SSD Nodes Learn Hosting plans →
Guides Matt ConnorBy Matt Connor

Install Conda on Ubuntu Server with Miniforge

Install conda on Ubuntu Server with Miniforge: pin a release, check its checksum, install in batch mode, and run services from an environment by absolute path.

How to install conda on Ubuntu Server

To install conda on Ubuntu Server, download a pinned Miniforge release and check it against the SHA-256 file published with that same release. Run the installer in batch mode into a directory in your home. Then run conda init for your own login user and turn off automatic activation of the base environment. Services never use any of that shell setup: a systemd unit calls the environment's Python by its absolute path.

Miniforge is a conda installer maintained by the conda-forge community. Its README says the conda-forge channel is set as the default and only channel. A channel is a package repository that conda downloads from. Every command in this guide runs as your normal login user. Only the systemd steps near the end need sudo.

Pick the Miniforge release and your architecture

This guide pins Miniforge 26.7.2-0, which was the latest release on GitHub on 20 October 2026. Its release notes say it updates to conda 26.7.2 and mamba 2.9.0 (mamba is a second conda-compatible client). Check the Miniforge releases page for a newer tag before you start. If there is one, you change one variable below.

Pinning a tag matters for two reasons. Every server you build gets the same installer, so a problem on one box can be reproduced on another. It also lets you check a checksum. Each release ships three files per Linux build: an installer with the version in its name, an installer without the version, and a .sha256 file. The .sha256 file exists only for the versioned installer. The README's quick-start URL points at latest/download/ and the unversioned file name, so it downloads a file with no checksum published next to it.

Miniforge publishes Linux builds for x86_64, aarch64, ppc64le and riscv64. The README calls the RISC-V build experimental. The asset names use the same spelling that uname -m prints, so the commands below pick the right file for your server. Install curl first if the image does not have it.

uname -m
sudo apt update && sudo apt install -y curl ca-certificates

If you have not yet decided whether conda is the right Python for this server at all, settle which Python to install on Ubuntu Server first. Conda adds a second Python next to Ubuntu's own, and you should make that choice on purpose.

Download the installer and verify its checksum

mkdir -p ~/src/miniforge && cd ~/src/miniforge
MF_VERSION=26.7.2-0
MF_FILE="Miniforge3-${MF_VERSION}-Linux-$(uname -m).sh"
MF_URL="https://github.com/conda-forge/miniforge/releases/download/${MF_VERSION}"
curl -fsSLO "${MF_URL}/${MF_FILE}"
curl -fsSLO "${MF_URL}/${MF_FILE}.sha256"
sha256sum -c "${MF_FILE}.sha256"

-f makes curl exit with an error on an HTTP failure, so a mistyped tag stops here. Without it, curl would save GitHub's error page under the installer's name. -L follows the redirect from the release URL to GitHub's asset storage.

A good result is one line ending in OK, such as ./Miniforge3-26.7.2-0-Linux-x86_64.sh: OK on an x86_64 server. The .sha256 file holds the hash, two spaces, and the path ./Miniforge3-26.7.2-0-Linux-x86_64.sh. sha256sum -c opens that exact path. The check works only when the installer sits in the current directory under its versioned name.

A mismatch prints FAILED and sha256sum: WARNING: 1 computed checksum did NOT match. Delete both files and download them again. Never run an installer that fails this check.

Be clear about what the check proves. The hash comes from the same GitHub release as the installer. It catches a truncated or corrupted download. It is not an independent signature, because anyone able to replace the installer on that page could replace the hash too. The guide on verifying downloads with checksums on Linux goes deeper on how checksum files work.

Run the Miniforge installer in batch mode

bash "${MF_FILE}" -b -p "$HOME/miniforge3"
"$HOME/miniforge3/bin/conda" --version

-b is batch mode, so the installer runs without asking questions. -p sets the prefix, the directory that holds everything the installer writes. The README does not state a default prefix, so always pass -p. Then you always know where conda lives. $HOME/miniforge3 belongs to you and needs no root. Run bash "${MF_FILE}" -h to list the other options.

The second line calls conda by its full path. It should print conda followed by a version number. That proves the install worked before you change anything in your shell.

Do not run the installer with sudo. The files would then belong to root, so every later conda install as your own user fails with a permission error.

Run conda init for your login user only

In batch mode the installer does not run conda init. That is why a fresh batch install answers conda: command not found: nothing has told your shell about conda yet. The README's fix is to call conda by its full path. conda init works by writing shell code into your shell's startup file, which for bash is ~/.bashrc. Look at the change first, then make it.

"$HOME/miniforge3/bin/conda" init --dry-run
"$HOME/miniforge3/bin/conda" init
source ~/.bashrc
type conda

--dry-run only displays what would be done. With no shell named, conda init sets up bash. Its default scope is --user, which means the current user only. type conda should now report conda as a shell function. You can also log out and back in instead of running source.

Never add --system here. That option initialises conda for all users on the system, which includes root. A later section explains why root should never see conda.

Why the base environment should not auto-activate

By default, once conda is initialised for your shell, every new shell activates the base environment. Activating an environment puts its bin directory at the front of your PATH. From then on, python3 and pip in every new shell mean base's copies, not Ubuntu's. Your prompt starts with (base) to show it.

That causes two problems. The first is that base is where conda itself lives. A pip install you meant for a project lands in base instead, and now your project's packages share a dependency tree with your package manager. The second is that any script starting with #!/usr/bin/env python3 picks the first python3 on PATH. Run a system or admin script from that shell and it gets conda's interpreter and conda's modules, not the ones apt installed.

Turn it off, then confirm the setting:

conda config --set auto_activate false
conda config --show auto_activate

In conda 26.7.2 this setting is named auto_activate, and its default is true. The Miniforge 26.7.2-0 README still shows the older name, auto_activate_base. Conda's source keeps that old name as an alias. Use the new name anyway, and read the result back with --show. conda config --set writes to your user ~/.condarc file by default.

Open a new shell. The prompt should no longer start with (base). You can see the PATH difference directly:

command -v python3
conda activate base
command -v python3
conda deactivate

The first line prints Ubuntu's interpreter, or nothing if the image has no Python. Inside base, the same command prints a path under $HOME/miniforge3/bin. Auto-activation made that same change in every shell you opened.

Check which channels and Python your conda really uses

Do not trust the README, or this guide, for what your install is configured to do. Ask conda.

conda config --show channels
conda config --show-sources
"$HOME/miniforge3/bin/python" --version

--show channels displays the channel list as conda calculated it from every configuration file. --show-sources displays each configuration file conda found and what each one sets. If the channel list is not what you expect, that second command tells you which file is responsible.

The last line asks base's own interpreter for its version, again by absolute path. The base Python changes between Miniforge releases, and the release notes for 26.7.2-0 do not name it, so read it from your own server. Treat it as the Python that runs conda. Do not build projects on it.

Create one environment per project

conda create -y -n webapp python=3.12
conda env list
conda run -n webapp which python
conda run -n webapp python --version

conda env list prints every environment and the directory it lives in. conda run -n webapp which python prints the absolute path of that environment's interpreter. Write that path down, because a service needs it.

You never need to activate an environment to use it from a script. Install into it by name with conda install -n webapp -y requests, and run a command inside it with conda run -n webapp. Prefer conda packages. When a package exists only on PyPI, run pip from inside the environment with conda run -n webapp python -m pip install <package>, so pip writes into that environment and nowhere else.

Why conda never belongs on root's PATH or a service's PATH

Root runs the commands that keep the operating system working. Ubuntu's Python tools expect Ubuntu's Python and the modules apt installed for it. If root's shell has conda initialised, any of those tools that looks up python3 on PATH gets conda's interpreter, which does not have those modules. Keep conda in the shells of the users who build software, and let root keep the system's Python.

A service has a different problem: it never reads your shell setup at all. systemd does not source ~/.bashrc, so there is no conda shell function and no activation. The systemd manual says the first word of ExecStart= must be an absolute path to an executable, or a simple file name that systemd resolves through a fixed search path set when systemd was compiled. It also says shell command lines are not directly supported.

That rules out the common mistakes. ExecStart=conda activate webapp && python app.py cannot work, because there is no shell to run && and conda is not on systemd's search path. ExecStart=python3 app.py does start, but it finds the system's interpreter, so the program fails on its first import from the environment. Putting the environment on the service's PATH with Environment= only hides which interpreter runs. An absolute path says it plainly.

Run a systemd service from a conda environment

A small program makes the result easy to see. It prints which interpreter is running it, then waits.

mkdir -p ~/app
printf 'import sys, time\nprint("running on", sys.executable, flush=True)\nwhile True:\n    time.sleep(60)\n' > ~/app/main.py
PY="$(conda run -n webapp which python)"
echo "$PY"

Now write the unit. The heredoc delimiter is not quoted, so the shell expands the variables before tee writes the file.

sudo tee /etc/systemd/system/webapp.service > /dev/null <<EOF
[Unit]
Description=webapp, run from the conda environment webapp
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=${USER}
WorkingDirectory=${HOME}/app
ExecStart=${PY} ${HOME}/app/main.py
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now webapp
systemctl status webapp --no-pager
sudo journalctl -u webapp -n 20 --no-pager

Read the unit back with systemctl cat webapp and check that ExecStart= holds a real path, not an empty variable. The journal should show running on followed by the same path that conda run printed. That line proves the service runs the environment's Python and not Ubuntu's. Type=simple fits a program that stays in the foreground, and how systemd service types differ covers the cases where it does not.

The journal line is what the program reports about itself. To ask the kernel instead, resolve the executable of the service's main process and compare it with the interpreter path:

sudo readlink -f "/proc/$(systemctl show -p MainPID --value webapp)/exe"
readlink -f "$PY"

Both lines should print the same file. In a conda environment bin/python is a symlink, so both show the versioned interpreter it points to. The first line needs sudo because the kernel lets only the process owner or root read another process's exe link.

If the unit fails with status=203/EXEC, systemd could not execute the ExecStart= path. Check that the path exists. Then check that the User= account can read every directory on the way to it. This matters when the service runs as a different user from the one who owns ~/miniforge3, because that user also needs to enter your home directory.

Calling the interpreter by path skips conda's activation step. If a package needs an environment variable that activation would normally set, add it to the unit with an Environment= line.

The licensing question, in Anaconda's own words

Many teams pick Miniforge because of Anaconda's commercial terms. Here is what the FAQ on Anaconda's legal page says, quoted rather than summarised. Asked whether Miniforge or Mambaforge requires a commercial license:

No, it does not. Both Miniforge and Mambaforge are not provided by Anaconda, and both installers include conda package manager, which is separate from Anaconda’s Distribution Installer (Anaconda’s proprietary package manager).

Asked whether conda itself requires a license:

No. conda (as detailed in this blog post) itself does not require a license. The Anaconda Distribution Installer is a separate piece of software and does require a commercial license for organizations over 200 employees/contractors.

On Miniconda, the same FAQ says "Miniconda does not require a commercial license for organizations over 200 employees/contractors until it is used to gain access to package updates." It also says "Miniconda points to Anaconda's Basic Repository to pull and update packages by default." That default repository is the difference that matters, and it is why this guide uses Miniforge.

Asked whether you need to pay for conda-forge:

No. Payment requirements in our Terms of Service do not apply to the user-uploaded packages and channels at anaconda.org, which includes the conda-forge channel.

Two lines from the same answer limit that. "While commercial fees don’t apply, our Terms of Service do still apply to conda-forge", and "payment requirements do apply if you access the Anaconda-provided channels on anaconda.org." So watch the channel list. If someone later adds an Anaconda-provided channel to a .condarc, conda config --show-sources shows which file did it. This section quotes an FAQ. It is not the Terms of Service text, and it is not legal advice.

Remove conda cleanly

Stop and remove any service that uses an environment first. Then reverse the shell change, check it, and delete the prefix.

sudo systemctl disable --now webapp
sudo rm /etc/systemd/system/webapp.service
sudo systemctl daemon-reload
"$HOME/miniforge3/bin/conda" init --reverse --dry-run
"$HOME/miniforge3/bin/conda" init --reverse
rm -rf "$HOME/miniforge3" "$HOME/.condarc"

Open a new shell afterwards. command -v conda should print nothing.

When venv or uv is the better tool on a server

Conda earns its place when a project needs libraries that are not Python, such as compiled scientific stacks or GPU runtimes, managed in one place. For a plain web app or a script that installs pure Python wheels from PyPI, conda is a second package manager you now have to patch and pin. A venv built on Ubuntu's Python, or a project managed with uv, gives systemd the same kind of absolute-path interpreter with less on the disk. The full trade-off, including how each one points a systemd service at the right environment, is in Python venv vs pipx vs uv on a server.

FAQ

Why does conda say "command not found" after a batch install?

A batch install with -b does not run conda init, so nothing has added conda to your shell. Call conda by its full path once, with ~/miniforge3/bin/conda init, then open a new shell or run source ~/.bashrc. type conda should then report a shell function. Never run conda init --system, which sets conda up for every user on the box.

Why does sha256sum say the Miniforge installer is missing?

The .sha256 file names the path ./Miniforge3-26.7.2-0-Linux-x86_64.sh, with the version in the file name. If you downloaded the unversioned Miniforge3-Linux-x86_64.sh, or ran the check from another directory, sha256sum -c cannot open that path and reports No such file or directory. Download the versioned installer from the tagged release and run the check in the same directory. The unversioned installer has no checksum file at all.

How do I run a systemd service from a conda environment?

Point ExecStart= at the environment's interpreter by its absolute path, which conda run -n <env> which python prints. systemd does not read your ~/.bashrc and does not support shell command lines, so conda activate inside a unit cannot work. A bare python3 resolves through systemd's fixed search path and finds the system interpreter instead.

Is Miniforge free to use at a company with more than 200 employees?

Anaconda's own legal FAQ answers "No, it does not" when asked whether Miniforge requires a commercial license, and says conda itself "does not require a license". The same FAQ says the conda-forge channel is not subject to its payment requirements, though its Terms of Service still apply. Payment requirements do apply to Anaconda-provided channels, so check your channel list with conda config --show channels to know which side you are on.

Should I use conda, venv or uv for a Python app on a VPS?

Use conda when the project depends on non-Python libraries or needs a Python version that Ubuntu does not package. For a pure Python app installed from PyPI, a venv on Ubuntu's Python or a uv-managed project is smaller and simpler to patch. Each of them lets a systemd unit call one interpreter by its absolute path.

#conda#miniforge#python#ubuntu#conda-forge#systemd