Installation
Condensa ships as a single self-contained binary: the web UI is embedded, the database is SQLite, and configuration is done through environment variables. Kubernetes is not required — it is only involved when the migration target is Awanio CEP.
Requirements
- Linux x86_64 server (a VM is fine)
- Network access from the Condensa server to:
- VMware vCenter/ESXi (port 443)
- the migration target — Awanio Cockpit or Vapor API (see Integration)
- Disk space for staged transfers (only used by the staged fallback and OVA/VHD uploads): size the staging directory for the largest disk you plan to migrate
Data path dependencies
Condensa moves disk data one of two ways, and each has its own prerequisite. The engine is chosen automatically per migration, so it is worth knowing which one your setup will get before you start a large transfer.
| Data path | How data flows | Warm migration | Prerequisite |
|---|---|---|---|
| Direct VDDK (preferred) | ESXi → target host | Supported | On the target Vapor host: nbdkit with the VDDK plugin, VMware's VDDK library, plus qemu-nbd and nbdfuse |
| Staged (fallback, Cockpit targets only) | ESXi → Condensa → target | Not supported | On the Condensa host: qemu-img (package qemu-utils) |
On the target host (Vapor): VDDK
Without it, warm migration is impossible and every migration falls back to the staged path. Install it through the Vapor UI (System → Third-party libraries) or the Ansible role, then confirm the badge reads installed. The migration log states which engine was chosen:
Target host supports VDDK; using direct VDDK pullIf instead you see Target host has no VDDK (nbdkit not found), the staged path is in use.
On the Condensa host: qemu-img
The staged path converts each exported disk to qcow2 inside Condensa, so qemu-img must be present there:
sudo apt-get install -y qemu-utils # Debian/Ubuntu
sudo dnf install -y qemu-img # RHEL/RockyCondensa refuses to start a staged migration without it rather than failing part-way through an export. The virtual appliance ships with it installed.
Prefer VDDK on the target
The staged path copies every byte twice (source → Condensa → target) and needs staging space for the largest disk. Installing VDDK on the target removes both costs and unlocks warm migration — worth doing before migrating anything large.
Reachability for the staged path
When the staged path is used, the target downloads the prepared disk from Condensa over CONDENSA_PUBLIC_URL. That URL must be reachable from the target host, not just from your browser: an address on a private or NAT'd network the target cannot route to will fail at the download step.
Injecting CONDENSA_PUBLIC_URL at install time
Both installers accept the value directly, so you never have to hand-edit the environment file on a fresh host. Pass --public-url (or export CONDENSA_PUBLIC_URL before running); precedence is --public-url > $CONDENSA_PUBLIC_URL > the host's auto-detected primary address.
# Self-installing binary
sudo ./condensa install --public-url http://condensa.example.com:8080
# Shell installer
sudo ./install.sh --public-url http://condensa.example.com:8080 ./condensa
# or via the environment:
sudo CONDENSA_PUBLIC_URL=http://203.0.113.10:8080 ./install.sh ./condensaThe auto-detected fallback is a best-effort guess from hostname -I; it is wrong on a NAT'd or multi-homed host, so set it explicitly at a customer site. Passing --public-url on an upgrade rewrites just that one line and keeps every secret in place — the fix when a first install guessed wrong.
Write the value so the target can use it verbatim: include the port unless it is the scheme default (443 for https, 80 for http), use https:// whenever CONDENSA_TLS is not off, and append the base-path prefix if you changed CONDENSA_BASE_PATH from /condensa.
| Situation | CONDENSA_PUBLIC_URL |
|---|---|
| DNS name, default HTTP port | http://condensa.example.com:8080 |
| Public IP | http://203.0.113.10:8080 |
TLS enabled (CONDENSA_TLS≠off) | https://condensa.example.com |
| Behind a base-path prefix | http://10.20.0.5:8080/condensa |
1. Install the binary
sudo install -m 0755 condensa-server /usr/local/bin/condensa-server
sudo mkdir -p /var/lib/condensa/staging /var/log/condensa /etc/condensa2. Generate the encryption key
Provider credentials (vCenter passwords, API tokens) are stored AES-256-GCM encrypted. The key is generated once and held by you — a leaked database does not leak infrastructure credentials.
condensa-server -generate-keyYou also need a JWT signing secret for authentication tokens. The server refuses to start without it (no insecure default), and changing it later invalidates existing sessions:
openssl rand -base64 48Store both in /etc/condensa/condensa.env:
sudo tee /etc/condensa/condensa.env <<'EOF'
CONDENSA_ENCRYPTION_KEY=<generated-encryption-key>
JWT_SECRET=<generated-jwt-secret>
EOF
sudo chmod 600 /etc/condensa/condensa.envKeep the key safe
Losing CONDENSA_ENCRYPTION_KEY means every provider credential must be re-entered. Back the file up alongside your other secrets.
3. Create the systemd unit
/etc/systemd/system/condensa.service:
[Unit]
Description=Condensa VM migration server
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=/usr/local/bin/condensa-server
Environment=PORT=8080
Environment=DB_PATH=/var/lib/condensa/condensa.db
Environment=CONDENSA_UPLOAD_DIR=/var/lib/condensa/staging
Environment=CONDENSA_LOG_FILE=/var/log/condensa/condensa.log
Environment=CONDENSA_PUBLIC_URL=http://condensa.example.com:8080
# condensa.env holds CONDENSA_ENCRYPTION_KEY and JWT_SECRET (chmod 600)
EnvironmentFile=/etc/condensa/condensa.env
Restart=on-failure
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable --now condensaEnvironment variables
| Variable | Required | Purpose |
|---|---|---|
CONDENSA_ENCRYPTION_KEY | Yes | AES-256 key for provider credentials at rest (-generate-key) |
JWT_SECRET | Yes | Signing key for auth tokens (≥32 bytes, e.g. openssl rand -base64 48); the server refuses to start without it |
PORT | No (default 8080) | HTTP listen port |
DB_PATH | No | SQLite database file path |
CONDENSA_PUBLIC_URL | For staged transfers / OVA | URL targets use to reach Condensa's disk-stream endpoint |
CONDENSA_UPLOAD_DIR | For staged transfers / OVA | Staging directory — size it for the largest staged disk |
CONDENSA_LOG_FILE | Recommended | Enables the UI's Logs page |
CONDENSA_ADMIN_USERNAME / CONDENSA_ADMIN_PASSWORD | No | Provision the first admin non-interactively (automation) |
CONDENSA_TLS | Recommended | off, self-signed, or on — see HTTPS |
CONDENSA_TLS_CERT / CONDENSA_TLS_KEY | With CONDENSA_TLS=on | PEM certificate chain and private key |
CONDENSA_TLS_HOSTS | No | Extra names/addresses a self-signed certificate should cover |
CONDENSA_UPDATE_URL | For in-place updates | Signed update feed — see Updating |
CONDENSA_UPDATE_PUBLIC_KEY | With CONDENSA_UPDATE_URL | Ed25519 key the feed is verified against |
HTTPS
The login form posts a password and every later request carries a session token, so serve the dashboard over TLS. Condensa offers three modes:
CONDENSA_TLS | Behaviour |
|---|---|
off (default) | Plain HTTP. Use only when a reverse proxy in front terminates TLS. |
self-signed | Condensa issues its own certificate and renews it automatically. |
on | Uses CONDENSA_TLS_CERT and CONDENSA_TLS_KEY. |
A value that is neither recognised nor empty is a startup error rather than a silent fall back to plaintext.
Self-signed certificates
Most installations are reached by IP address on a private network, where no certificate authority will issue anything. self-signed covers that case:
CONDENSA_TLS=self-signedThe certificate is stored next to the database, covers loopback, the hostname and every interface address, and is re-issued automatically when it nears expiry or when the host gains an address it does not name. Browsers warn on first visit until you trust it — a one-time click, against sending credentials in the clear on every request.
Add names that are not derived from the interfaces:
CONDENSA_TLS_HOSTS=condensa.internal.example.com,10.20.0.5Certificates you supply
CONDENSA_TLS=on
CONDENSA_TLS_CERT=/etc/condensa/tls/fullchain.pem
CONDENSA_TLS_KEY=/etc/condensa/tls/privkey.pemThe service user must be able to read both files.
Transfers that pull from Condensa
Two migration paths have the target download disk images from Condensa over CONDENSA_PUBLIC_URL: the staged fallback to Cockpit, and Awanio CEP (CDI). Those downloaders verify certificates, so a self-signed certificate will be refused unless the target trusts it. Use a CA-signed pair for those paths.
Direct VDDK migrations — the default for Cockpit and Vapor targets — pull from VMware rather than from Condensa and are unaffected.
Remember to set CONDENSA_PUBLIC_URL to an https:// URL when TLS is on.
4. First run
Open http://<server>:8080. On a fresh database Condensa shows a setup wizard that creates the first administrator account and logs you in. For scripted deployments, set CONDENSA_ADMIN_USERNAME and CONDENSA_ADMIN_PASSWORD instead — the admin is created at startup.
After setup:
- create additional accounts under Users (admin-only). The
memberrole is an operator role: it can view providers and create, start, and cancel migrations, while provider management, deletions, user administration, and the audit log stay admin-only. - every security-relevant action (logins and failures, user and provider changes, migration lifecycle) is recorded in the Audit Log tab.
5. Connect providers
Continue with Integration to register your VMware source and an Awanio Cockpit or Vapor target.
Updating
Updating never touches /var/lib/condensa or /etc/condensa, so your providers, their encrypted credentials, migration history, users and both secrets carry over. You do not reconfigure anything.
From the dashboard
When a newer release is published, administrators see a notice with an Install update button. Condensa downloads the release, verifies it, installs it and restarts — a few seconds of downtime. The button refuses while a migration is running, because a restart would abandon it.
Installations set up with Condensa 1.1.8 or later are preconfigured to check the enterprise portal — there are no keys and nothing to set up. The source is one line in /etc/condensa/condensa.env; blank it to disable update checks entirely:
CONDENSA_UPDATE_URL=https://enterprise.awan.io/api/v1/products/condensa/releases/latestInstallations set up before 1.1.8 gain the same behaviour after their next manual upgrade; the line above can also be added by hand.
From the command line
sudo condensa update --check # report what is available, change nothing
sudo condensa update # install the newest release and restart
sudo condensa update --to 1.1.7 # install a specific releaseAdd --force to update while a migration is running — it will be interrupted and fail.
What the updater checks
Before anything replaces the binary that currently works:
- the release must come from a trusted source — TLS to the configured portal, or, for air-gapped feeds, an Ed25519 signature that verifies against your configured keys;
- the downloaded file's SHA-256 must match the one the feed publishes;
- the downloaded binary must run and report the version it promised — this catches a truncated or wrong-architecture download that would otherwise leave the service unable to start;
- no migration may be running, unless forced.
The binary it replaces is kept alongside as condensa.previous, so a rollback is a copy and a restart.
Replacing the binary by hand
Still supported, and unchanged — database migrations run at startup:
sudo install -m 0755 condensa /usr/local/bin/condensa
sudo systemctl restart condensaAir-gapped installations
Hosts that cannot reach the enterprise portal still update in place, two ways.
Manual (simplest): bring the release tarball in through your controlled transfer channel, verify its SHA-256 against the value shown on the portal's release page, then:
tar -xzf condensa-<version>-linux-amd64.tar.gz
sudo ./condensa installThe upgrade keeps the database, providers, credentials and settings.
Automatic, from an internal mirror: Awanio supplies, as part of the delivery, a signed feed (updates.signed.json) and the public keys that verify it. Host the feed and the release tarballs on an internal https server, then configure each installation:
CONDENSA_UPDATE_URL=https://releases.internal.example.com/condensa/updates.signed.json
CONDENSA_UPDATE_PUBLIC_KEY=<key A>,<key B>If the mirror's certificate comes from an internal CA, add that CA to the OS trust store of each Condensa host. The feed is Ed25519-signed, so a compromised mirror cannot alter what installations will accept; every other check the updater performs applies unchanged.