What multi-user mode changes
The multi-user administrator guide gives each account its own boards and gallery, sessions of 24 hours (7 days with “Remember me”), and a queue that interleaves users so one large batch cannot hold the card. Administrators install, edit and delete models; everyone else can use them but not change them. There is no per-user model access and no audit log yet. Deleting a user leaves their images in the database and under outputs/images, visible to administrators.
The guide also says the software has not had a rigorous security audit, and recommends HTTPS, access limited to trusted networks, and regular backups. This page follows that advice. For a node graph instead, read ComfyUI on a GPU server; for the WebUI route, the Stable Diffusion server write-up.
Card memory, system RAM and card generation
These rows are from the project's hardware requirements page for Windows and Linux, which calls them rough guidelines for best performance. RAM matters because InvokeAI keeps a RAM copy of each loaded model's weights by default (keep_ram_copy_of_weights in config_default.py).
| Model family | Resolution | Min VRAM | Min RAM | Note from the table |
|---|---|---|---|---|
| SD1.5 | 512x512 | 4 GB | 8 GB | Nvidia 10xx and newer |
| SDXL | 1024x1024 | 8 GB | 16 GB | Nvidia 20xx and newer |
| FLUX.1 | 1024x1024 | 10 GB | 32 GB | Nvidia 20xx and newer |
| FLUX.2 Klein 9B | 1024x1024 | 24 GB | 32 GB | Nvidia 40xx; FP8 works with 12 GB+ |
| Wan 2.2 TI2V-5B (video) | 1280x720 | 8 GB | 16 GB | Diffusers needs 12 GB+ |
Card generation matters as much as memory here. The v6.14.2 CUDA image is built on torch 2.7.1 for CUDA 12.8 (pyproject.toml), and PyTorch's 2.7 build script (build_cuda.sh) compiles those wheels for sm_75 and newer only. Every card on our list older than Turing therefore cannot generate on this image, whatever the SD1.5 row says: the Quadro 3000M, K3100M, M3000M, P5000 and P6000, the Tesla M6, M60, P4, P40, P100 and V100, the GTX 1070M, 1070, 1070 Ti, 1080 and 1080 Ti, and the TITAN V. Turing (RTX 20xx, T4, Quadro RTX) is the floor. Our rule of thumb for a team is to size for the largest model anyone uses, then add RAM as you add cards. The YAML config page says the RAM copy of a model is shared between the GPUs' caches, but different GPUs holding different models raise RAM use.
The queue on one card or several
On one card, jobs run one at a time. session_queue_mode: round_robin (the default) serves each user one job before anyone gets a second; FIFO runs them in order. v6.14.0 added multi-GPU support, where generation_devices: auto runs one session per CUDA GPU concurrently, still round-robin across users; a list such as [cuda:0, cuda:1] pins cards. The admin guide's FAQ still calls this a future feature; the release notes and config reference supersede it.
A second card in one chassis is quoted by hand, per card, since slot clearance and power decide how many fit; our multi-GPU servers page covers which platforms take more than one.
The machine under it
On our GPU dedicated servers the card sits in a PCIe slot of a machine rented to one account, handed straight to your OS, with no vGPU profile, MIG partition or scheduler above yours. Every core, all the memory, both disks and root come with it, plus the server's out-of-band console, an iLO, iDRAC or BMC controller on a separate management network, published in the dashboard once you add a console login.
The port is unmetered, so multi-gigabyte checkpoints cost nothing per byte. Memory and disks are chosen in the configurator. A racked machine that already holds its card, taken as it stands, is handed over about 30 minutes after approval of the order; a build with the card you pick takes 4 to 24 hours. Pick the city your team is nearest to. The image README limits GPU acceleration to x86_64, so ARM machines are out.
Install with Docker on a pinned tag
Start from Ubuntu with the NVIDIA driver, Docker Engine 28+ and the NVIDIA Container Toolkit, installed per NVIDIA's guide; as our rule, nvidia-smi should report CUDA 12.8 or higher. The block turns on ufw for SSH only (ufw allow OpenSSH opens port 22 alone, so allow a moved SSH port first), checks the Engine version, runs NVIDIA's sample CUDA container, writes the compose file unless one exists, checks it keeps the loopback port, multi-user mode, a pinned tag and the GPU reservation, then starts InvokeAI. Look for START-OK.
D=/opt/invokeai
sudo sed -i 's/^IPV6=.*/IPV6=yes/' /etc/default/ufw && sudo ufw allow OpenSSH && sudo ufw --force enable \
&& v=$(sudo docker version -f '{{.Server.Version}}') && [ "${v%%.*}" -ge 28 ] \
&& sudo docker run --rm --runtime=nvidia --gpus all ubuntu nvidia-smi && sudo mkdir -p $D/root \
&& { [ -e $D/compose.yaml ] || sudo tee $D/compose.yaml >/dev/null <<'EOF'
services:
invokeai:
image: ghcr.io/invoke-ai/invokeai:v6.14.2-cuda
container_name: invokeai
restart: unless-stopped
environment:
INVOKEAI_MULTIUSER: "true"
INVOKEAI_STRICT_PASSWORD_CHECKING: "true"
ports:
- "127.0.0.1:9090:9090"
volumes:
- /opt/invokeai/root:/invokeai
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
EOF
} && grep -q '"127.0.0.1:9090:9090"' $D/compose.yaml && grep -q 'INVOKEAI_MULTIUSER: "true"' $D/compose.yaml \
&& grep -q 'invokeai:v[0-9]' $D/compose.yaml && grep -q 'capabilities: \[gpu\]' $D/compose.yaml \
&& sudo docker compose -f $D/compose.yaml up -d && echo START-OK \
|| echo "STOP - firewall, Docker 28+, GPU test, or an existing compose.yaml without the loopback port, multi-user line, pinned tag or GPU line"
The tag is pinned because build-container.yml marks latest only on main-branch builds, now the v7.0 alphas, while each release gets its own tag. The admin guide says to back up before upgrading, since upgrades migrate the database.
Our edits to docker/docker-compose.yml, in full: the service renamed from invokeai-cuda to invokeai (use that name with docker compose exec); the pinned tag instead of latest; no build section; no env_file or HF_HOME variable and cache volume, so the entrypoint keeps that cache in the root folder; the INVOKEAI_ROOT, HOST_INVOKEAI_ROOT and INVOKEAI_PORT substitutions dropped, since the image sets /invokeai and 9090; the port bound to 127.0.0.1; the long-form bind with create_host_path replaced by a fixed bind path; tty and stdin_open dropped; count: all GPUs instead of 1; the CPU and ROCm services and YAML anchor removed; container_name, a restart policy and two multi-user variables added. Any setting can be an INVOKEAI_ variable, per the YAML config page.
A mapping with no address publishes on 0.0.0.0 and [::], and published ports skip ufw on IPv4 (on IPv6 too when Docker's own IPv6 networking is on); loopback-only publishing is local-only on Engine 28 and later, hence the version check. docker-entrypoint.sh runs the app as UID 1000 and chowns the root folder at every start.
The first administrator, created privately
Until an administrator exists, the setup endpoint in auth.py accepts the first account from whoever calls it.
- From the laptop you work on,
ssh -N -L 9090:127.0.0.1:9090 you@server-ipopens the tunnel through your own SSH account; 9090 is the only port InvokeAI listens on. - Browse to
http://localhost:9090and complete Administrator Setup. The email becomes the username; with strict checking on, the password needs at least 8 characters with upper case, lower case and a number. - Install models as the administrator, then add colleagues in the web interface or with
sudo docker exec -it -u 1000 invokeai invoke-useradd, which prompts for details. A--passwordargument would be visible inpsand shell history.
HTTPS for the team, allow-listed
The admin guide fronts InvokeAI with an HTTPS proxy that passes WebSocket upgrades and advises limiting access to trusted networks. Point DNS for the hostname at this machine before running it, and give it an A record only, or add a matching ufw rule for the team's IPv6 prefix on 443. Caddy from the Ubuntu 24.04 (or later) archive does the rest. The block refuses until the placeholders change and /api/v1/auth/status shows multi-user mode with an admin. Port 80 stays open so Caddy can obtain and renew its certificate; 443 opens to your team's address only. Caddy runs on the host, so ufw filters it on IPv4 and IPv6.
H=invoke.example.com; T=203.0.113.10 # your hostname, your team's public IPv4
[ "$H" != invoke.example.com ] && [ "$T" != 203.0.113.10 ] \
&& curl -s http://127.0.0.1:9090/api/v1/auth/status | grep -q '"setup_required":false,"multiuser_enabled":true' \
&& sudo apt-get update && sudo apt-get install -y caddy \
&& printf '%s {\n\treverse_proxy 127.0.0.1:9090\n}\n' "$H" | sudo tee /etc/caddy/Caddyfile >/dev/null \
&& sudo ufw allow 80/tcp && sudo ufw allow from "$T" to any port 443 proto tcp \
&& sudo systemctl reload caddy && echo PROXY-OK \
|| echo "STOP - placeholders, InvokeAI not answering on 127.0.0.1:9090 in multi-user mode with an admin, or the apt, Caddyfile, ufw or reload step above"
The guide also suggests rate-limiting /api/v1/auth/login. For people on changing addresses, a private network beats a long allow-list; see our WireGuard VPN server page.
Backups, updates and restores
The admin guide's complete backup is databases/, models/, outputs/ and invokeai.yaml; archiving the root folder and the compose file with InvokeAI stopped covers it, and models dominate the size. The first line sets the variables; run it again in any new shell, then one part at a time. A backup counts only when its archive lists the database and gets a .ok marker; only then are older archives pruned, keeping the three newest good ones. If part 1 halts after BACKUP-OK, the new archive stands and only the pruning went wrong; without BACKUP-OK, any unverified archive it started is deleted. Copy archives off the machine yourself with plain rsync -a, never --delete.
D=/opt/invokeai; B=/var/backups/invokeai; R=$D/root; Y=$D/compose.yaml; C="sudo docker compose -f $Y"
# 1. backup - stop, archive, verify, prune
f=$B/invokeai-$(date +%F-%H%M%S).tar.gz; sudo install -d -m 700 $B && $C stop \
&& sudo tar -czf $f -C $D compose.yaml root && L=$(sudo tar -tzf $f) \
&& grep -qx root/databases/invokeai.db <<< "$L" && sudo touch $f.ok && echo BACKUP-OK $f \
&& sudo sh -c "ls -1t $B/*.ok | tail -n +4 | while read o; do rm -f \"\$o\" \"\${o%.ok}\"; done" \
|| { sudo test -e $f.ok || sudo rm -f $f; echo "STOP - see above; archive kept only if BACKUP-OK printed"; }; $C start
# 2. restore (or undo an update) - set A
A=$B/invokeai-DATE.tar.gz; sudo test -e "$A.ok" && ! sudo test -e $R.previous \
&& ! sudo test -e $Y.previous && L=$(sudo tar -tzf "$A") && grep -qx root/databases/invokeai.db <<< "$L" \
&& sudo mkdir -p $D && { [ ! -e $Y ] || $C down; } && { sudo docker rm -f invokeai 2>/dev/null; true; } \
&& { [ ! -e $Y ] || sudo mv $Y $Y.previous; } && { [ ! -e $R ] || sudo mv $R $R.previous; } \
&& sudo tar -xzf "$A" -C $D && $C up -d && echo RESTORE-OK \
|| echo "STOP - check A, A.ok, .previous; none = nothing moved, else they hold your data"
# 3. undo 2 - set U=yes
U=no; [ "$U" != yes ] || { sudo test -d $R.previous/databases && sudo test -f $Y.previous \
&& { $C down; true; } && sudo rm -rf $R $Y && sudo mv $R.previous $R && sudo mv $Y.previous $Y \
&& $C up -d && echo ROLLED-BACK || echo "STOP - undo halted, both .previous copies must exist"; }
To update, back up, then run `N=vX.Y.Z-cuda; [ "$N" != vX.Y.Z-cuda ] && sudo find $B -name '*.ok' -mmin -60 | grep -q . && sudo docker pull ghcr.io/invoke-ai/invokeai:$N && sudo sed -i "s#invokeai:v.*#invokeai:$N#" $D/compose.yaml && grep -q "invokeai:$N\$" $D/compose.yaml && $C up -d && echo UPDATE-OK || echo "STOP - update halted" with N set to a released -cuda tag. It refuses without a BACKUP-OK from the last hour and pulls while the old version still serves. If the new version misbehaves, part 2 with the pre-update archive restores the data and the old tag. On a rebuilt machine, run the install block, paste the block's first line, run sudo install -d -m 700 $B, copy the archive and its .ok` marker in with sudo, then run part 2. Delete the .previous copies when satisfied, or part 2 refuses next time.
When it is the wrong setup
One person making a few images a week does not need a monthly machine. A public service for strangers is a different product: InvokeAI has no outside identity provider yet (OAuth2 and OpenID Connect are planned), no per-user model limits and no audit log. InvokeAI is under Apache 2.0, which in our general reading (not legal advice) asks that when you pass copies on you include the licence text, keep its copyright and attribution notices (and a NOTICE file, if a release ever ships one) and mark files you change. Each model carries its own licence, which decides what the team may do with the images.
Questions
Can two people generate simultaneously on a single card?
Both can queue work, but on one card the jobs run one after another, one per user per turn by default. Parallel generation needs a second card, which v6.14.0 supports through generation_devices.
Is leaving it in single-user mode behind a firewall acceptable?
For one person on an SSH tunnel, yes. Single-user mode has no login, so anyone who reaches the port can install models and see every image. Once a second person or a proxy is involved, switch multi-user mode on.
Why does InvokeAI run on the CPU instead of the GPU?
Usually the container cannot see the card: check sudo docker exec invokeai nvidia-smi, then the Container Toolkit and the compose deploy block. On a pre-Turing card the torch build itself has no kernels for it. A device setting pinned to cpu also overrides the default auto.
Does it work with an AMD Instinct card?
The hardware page supports AMD on Linux only and warns of occasional bumps with torch on ROCm. The project publishes -rocm image tags and a ROCm service in its compose file; the compose file here is for NVIDIA.