Axolotl with Docker: fine-tune open LLMs from one YAML file (Linux, Windows, Mac)
Before you start
- 22 min read
Boxes marked Run in terminal are what you type; plain text explains why. Check what you need first

What you get in one minute#
Axolotl is a free, open-source tool that fine-tunes a language model from one YAML file: you name the base model, your data and the method (LoRA, QLoRA or full), and the same file then drives training, testing, merging and export.
Logo: the official Axolotl project (github.com/axolotl-ai-cloud/axolotl)
Axolotl suits people who want control: a text config you can keep in git, many training methods, and a path from one GPU to many. There is no browser app; you work in the terminal. This guide uses the official Docker image, so you do not fight Python, PyTorch and CUDA versions yourself.
| Current version | Axolotl 0.21.1 (GitHub release and PyPI, October 2026) |
| Image | axolotlai/axolotl:0.21.1-py3.12-cu130-2.13.0 (Python 3.12, CUDA 13.0, PyTorch 2.13.0), about 8.9 GB download for amd64 |
| Licence | Apache 2.0 |
| Time needed | About 45 minutes plus downloads (our estimate; the docs give no run time) |
| Memory | For a 1-3B model, the docs say 6-8 GB (QLoRA) or 8-12 GB (LoRA). Full table under "Performance and resources" |
| Ports | None for training. The optional cloud image runs JupyterLab on 8888 |
| Official docs | docs.axolotl.ai and github.com/axolotl-ai-cloud/axolotl |
At a glance:
- What it is: a command-line trainer:
axolotl train,inference,merge-lora,export. - What you need: an NVIDIA GPU (Ampere or newer; the docs also name AMD GPUs, not covered here), Docker, your data as JSONL.
- What you get: a LoRA adapter, a merged model, and optionally a GGUF file for Ollama.
- Mac: only partly usable (LoRA and full training; no QLoRA). See Step 1.
- Privacy: training runs on your machine. Telemetry is on by default (we turn it off).
- Status: alive. Release 0.21.1 is from October 2026 and the README lists updates through October 2026. The project moved to the
axolotl-ai-cloudGitHub organisation; the oldOpenAccess-AI-Collectiveaddress redirects there.
Fine-tune or RAG?#
Fine-tune to change how a model answers. Use RAG when it needs your facts and they change. For answers from your own documents, see our Qdrant guide. With only a few dozen examples, try a better prompt first. Many people use both.
Against Unsloth: Axolotl is a YAML file and a terminal, with many methods (SFT, LoRA, QLoRA, full, DPO, KTO, ORPO, GRPO) and a path to several GPUs. Our Unsloth guide is a browser app on one GPU. Pick Axolotl for a repeatable config or more GPUs, Unsloth for the quickest first result.
How it fits together#
You write one YAML file; Axolotl reads it for training, testing, merging and export.
Drawn from the Axolotl docs (Quickstart, Inference and Merging, Exporting to GGUF, Telemetry), November 2026. Red is data sent out by default.
Before you start#
| System | What you need |
|---|---|
| Linux | NVIDIA GPU (Ampere or newer), driver 580 or newer (the image uses CUDA 13.0; NVIDIA says CUDA 13.x needs 580+), Docker Engine with Compose, NVIDIA Container Toolkit |
| Windows | NVIDIA GPU, a current NVIDIA driver, Docker Desktop with the WSL 2 engine. The docs: "We recommend using WSL2 (Windows Subsystem for Linux) or Docker" |
| Mac | No NVIDIA or AMD GPU and no Docker GPU. Native install only, with limits (Step 1) |
docker --version docker compose version nvidia-smi
- [ ]
nvidia-smishows your GPU and a driver of 580 or newer - [ ] About 40 GB free disk for the image, the model and the outputs (our estimate)
- [ ] Your training data as JSONL (Step 4 shows the format)
- [ ] Only for the optional HTTPS section: a domain, DNS and ports 80/443
Tested on 2026-11-05: Linux = not run, Windows = not run, Mac = not run. Every command was checked against the official docs (see Sources).
Step 1: Let Docker see your GPU#
If docker run --rm --gpus all ubuntu nvidia-smi prints your GPU, this step is done. That is NVIDIA's own check.
Linux#
Install Docker, then the NVIDIA Container Toolkit. These are NVIDIA's apt steps for Ubuntu and Debian (other distributions are on the same page):
sudo apt-get update && sudo apt-get install -y --no-install-recommends ca-certificates curl gnupg2
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg \
&& curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt-get update
sudo apt-get install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
docker run --rm --gpus all ubuntu nvidia-smi
Prefer no Docker? The README route is uv: install uv, export UV_TORCH_BACKEND=cu130, uv venv --python 3.12, source .venv/bin/activate, uv pip install axolotl[deepspeed]. Then drop the Compose prefix from the commands below.
Windows (PowerShell)#
- Install the latest NVIDIA Windows driver.
- Install Docker Desktop and update WSL:
wsl --update
- In Docker Desktop open Settings > General, tick Use the WSL 2 based engine, and wait until the engine runs. Docker's GPU page requires the WSL 2 backend and a driver with WSL 2 GPU support.
docker run --rm --gpus all ubuntu nvidia-smi
You can also work inside a WSL 2 Linux distribution with the Linux steps.
Mac (Terminal)#
Docker on a Mac cannot pass the Apple GPU into the image, so there is no container route. The Axolotl docs list what works: all models, full training, LoRA and sample packing. Not supported yet: FP16 and BF16, flash-attn, xformers, bitsandbytes (no 4-bit or 8-bit loading), QLoRA and DeepSpeed. The native install from the docs:
curl -LsSf https://astral.sh/uv/install.sh | sh git clone https://github.com/axolotl-ai-cloud/axolotl.git cd axolotl uv venv --python 3.12 source .venv/bin/activate uv pip install -e '.'
We did not run this on a Mac. For serious training use an NVIDIA machine or a rented GPU.
Check it worked. Linux and Windows print the normal nvidia-smi table with your GPU. On a Mac, axolotl --help lists the commands.
Tip: A driver that is too old is the quiet failure. The images are built for CUDA 13.0, which needs driver 580 or newer. Check
nvidia-smibefore you pull a 9 GB image.
Step 2: Create the folders and the Compose file#
Everything lives in ~/docker_volumes/axolotl. Configs, outputs and the model cache stay on your disk, so deleting the container loses nothing.
Linux#
mkdir -p ~/docker_volumes/axolotl/{configs,outputs,huggingface,backups}
cd ~/docker_volumes/axolotl
umask 077
printf '# Only for gated models: HF_TOKEN=<YOUR_HF_TOKEN>\n' > .env
chmod 600 .env
Windows (PowerShell)#
$d = "$HOME\docker_volumes\axolotl" New-Item -ItemType Directory -Force "$d\configs","$d\outputs","$d\huggingface","$d\backups" | Out-Null Set-Content -Path "$d\.env" -Value '# Only for gated models: HF_TOKEN=<YOUR_HF_TOKEN>' Set-Location $d
Mac (Terminal)#
Skip this step. Work in the cloned axolotl folder and run mkdir configs there.
Create docker-compose.yml in that folder (Linux and Windows). It is the docs' docker run command as Compose, without the obsolete version: line:
services:
axolotl:
image: axolotlai/axolotl:0.21.1-py3.12-cu130-2.13.0
working_dir: /workspace/axolotl
ipc: host
ulimits:
memlock: -1
stack: 67108864
env_file: .env
environment:
AXOLOTL_DO_NOT_TRACK: "1"
volumes:
- ./configs:/workspace/axolotl/configs
- ./outputs:/workspace/axolotl/outputs
- ./huggingface:/root/.cache/huggingface
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
The tag is pinned, so a new release never changes your setup. ipc and ulimits are the docs' --ipc=host, --ulimit memlock=-1 and --ulimit stack=67108864. working_dir is the folder the Docker docs name for the Axolotl code, so relative paths in your YAML start there. AXOLOTL_DO_NOT_TRACK turns telemetry off. There is no restart: line: this is a job run with docker compose run --rm, not a service that stays up.
Pull the image (the big download), then check it:
docker compose pull docker compose run --rm axolotl nvidia-smi docker compose run --rm axolotl axolotl --help
docker compose pull docker compose run --rm axolotl nvidia-smi docker compose run --rm axolotl axolotl --help
Check it worked. The first command prints your GPU, the second lists the commands (train, inference, merge-lora and more).
Tip: On Linux the container runs as root, so files in
outputsbelong to root. To edit or back them up as yourself:sudo chown -R $USER ~/docker_volumes/axolotl/outputs.
Step 3: Smoke-test with the official example#
Run the project's own example first. If it starts training, the stack works, and any later error is your config.
Linux#
docker compose run --rm axolotl bash -c "axolotl fetch examples && axolotl train examples/llama-3/lora-1b.yml"
Windows (PowerShell)#
docker compose run --rm axolotl bash -c "axolotl fetch examples && axolotl train examples/llama-3/lora-1b.yml"
Mac (Terminal)#
Skip this. The example uses flash_attention_2 and an 8-bit bitsandbytes optimizer, which the Mac page lists as unsupported. Use the Mac changes in Step 4.
The example trains a LoRA on the 1B model NousResearch/Llama-3.2-1B with the public teknium/GPT4-LLM-Cleaned dataset for one epoch. The docs give no run time and we did not run it, so treat it as a check.
Check it worked. The model and dataset download, then each step prints a log line with the loss (the example sets logging_steps: 1). After a few lines stop with Ctrl+C. It saves once per epoch, so stopping early leaves no adapter; that is fine here.
Tip: Read the first 30 lines of every run. Axolotl warns at start-up about problems like a missing pad token or an untrained end-of-turn token, and they are easy to scroll past. In issue #4128 the warning was there and the tuned model still never stopped talking.
Step 4: Write your data and your config#
One JSONL file for the data, one YAML file for everything else.
The simplest data shape is alpaca: one JSON object per line with instruction, input (may be empty) and output. The sample only shows the shape. A real run needs far more rows; with val_set_size: 0.1 Axolotl holds back 10 percent for evaluation (the default is none).
Linux#
cd ~/docker_volumes/axolotl
cat > configs/my_data.jsonl <<'EOF'
{"instruction": "Classify this text", "input": "I love this!", "output": "positive"}
{"instruction": "Classify this text", "input": "Not good at all", "output": "negative"}
{"instruction": "Classify this text", "input": "Works fine, nothing special", "output": "neutral"}
{"instruction": "Classify this text", "input": "Best purchase this year", "output": "positive"}
{"instruction": "Classify this text", "input": "It broke after one day", "output": "negative"}
{"instruction": "Classify this text", "input": "Does what it says", "output": "neutral"}
EOF
Windows (PowerShell)#
Plain UTF-8 text, one JSON object per line:
Set-Location "$HOME\docker_volumes\axolotl"
Set-Content -Path configs\my_data.jsonl -Encoding utf8NoBOM -Value @'
{"instruction": "Classify this text", "input": "I love this!", "output": "positive"}
{"instruction": "Classify this text", "input": "Not good at all", "output": "negative"}
{"instruction": "Classify this text", "input": "Works fine, nothing special", "output": "neutral"}
{"instruction": "Classify this text", "input": "Best purchase this year", "output": "positive"}
{"instruction": "Classify this text", "input": "It broke after one day", "output": "negative"}
{"instruction": "Classify this text", "input": "Does what it says", "output": "neutral"}
'@
Mac (Terminal)#
Same as Linux, inside the cloned axolotl folder.
Now the config, saved as configs/my_training.yml with any editor. It is the official lora-1b.yml with your local file, sample_packing off (it suits larger data) and three epochs, as in the Quickstart's own custom example:
base_model: NousResearch/Llama-3.2-1B
datasets:
- path: configs/my_data.jsonl
type: alpaca
output_dir: ./outputs/lora-out
adapter: lora
lora_r: 16
lora_alpha: 32
lora_dropout: 0.05
lora_target_linear: true
sequence_len: 2048
micro_batch_size: 2
gradient_accumulation_steps: 2
num_epochs: 3
learning_rate: 0.0002
lr_scheduler: cosine
warmup_ratio: 0.1
optimizer: adamw_8bit
bf16: auto
gradient_checkpointing: true
attn_implementation: flash_attention_2
logging_steps: 1
special_tokens:
pad_token: "<|end_of_text|>"
| Setting | What it does |
|---|---|
adapter: lora | Trains small add-on layers instead of every weight. Remove it for full fine-tuning. For QLoRA use load_in_4bit: true with adapter: qlora (the Quickstart's tip) |
sequence_len | Longest example in tokens. The default is 512 |
micro_batch_size | Examples per step. Lower it when memory runs out |
attn_implementation | flash_attention_2 needs Ampere or newer. On an older GPU sdpa is also a valid value (our suggestion from the config reference) |
special_tokens.pad_token | This tokenizer has no padding token (see Troubleshooting) |
Mac changes (our reading of the Mac page, not run): swap flash_attention_2 for sdpa, adamw_8bit for adamw_torch, and delete bf16: auto, because the page lists flash-attn, bitsandbytes and BF16/FP16 as unsupported.
Check it worked. ls configs (Linux, Mac) or Get-ChildItem configs (Windows) lists my_data.jsonl and my_training.yml.
Tip: For a local file write just
path: configs/my_data.jsonl. Old recipes usepath: jsonwithdata_files:; issue #2709 shows Axolotl then looking for the file on Hugging Face and failing with a 401.
Which data format do I have?#
The docs' own flow chart, shortened:
| Your data | Use | Config |
|---|---|---|
Already tokenized (input_ids, attention_mask, labels) | Pre-tokenized | type: left empty |
segments of text and label you mark yourself | Template-free | type: input_output |
Chats: {"messages": [{"role": "...", "content": "..."}]} | Conversation | type: chat_template |
| Instruction and answer pairs | Instruction | type: alpaca and other named formats |
| Raw text for continued pre-training | Pre-training | type: completion |
For chat data, add eot_tokens when your template's end-of-turn token differs from the tokenizer's end token (issue #4128: without it the model never stopped). The token below is ChatML's:
datasets:
- path: configs/my_chats.jsonl
type: chat_template
roles_to_train: ["assistant"]
chat_template: tokenizer_default
eot_tokens:
- "<|im_end|>"
Old sharegpt data (conversations with from and value) works through chat_template with field_messages: conversations and message_property_mappings (role: from, content: value).
Step 5: Train#
Linux#
cd ~/docker_volumes/axolotl docker compose run --rm axolotl axolotl train configs/my_training.yml
Windows (PowerShell)#
Set-Location "$HOME\docker_volumes\axolotl" docker compose run --rm axolotl axolotl train configs/my_training.yml
Mac (Terminal)#
In the cloned folder, with the virtual environment active:
axolotl train configs/my_training.yml
Check it worked. The run ends without an error and the output folder holds the adapter:
ls outputs/lora-out
Get-ChildItem outputs\lora-out
Look for adapter_model.safetensors (the name used in issue #4128). Six rows only prove the pipeline works; the loss and answers mean something only with real data.
Tip: For a quick trial on a big dataset add
max_steps: 30to the YAML. The config reference says it wins overnum_epochs.
Step 6: Test the model and merge the adapter#
The same YAML is used for inference and merging (the Quickstart says so).
Linux#
docker compose run --rm axolotl axolotl inference configs/my_training.yml --lora-model-dir="./outputs/lora-out" --chat docker compose run --rm axolotl axolotl merge-lora configs/my_training.yml --lora-model-dir="./outputs/lora-out"
Windows (PowerShell)#
docker compose run --rm axolotl axolotl inference configs/my_training.yml --lora-model-dir="./outputs/lora-out" --chat docker compose run --rm axolotl axolotl merge-lora configs/my_training.yml --lora-model-dir="./outputs/lora-out"
Mac (Terminal)#
Same commands, without the docker compose run --rm axolotl prefix:
axolotl inference configs/my_training.yml --lora-model-dir="./outputs/lora-out" --chat axolotl merge-lora configs/my_training.yml --lora-model-dir="./outputs/lora-out"
Check it worked. Type a message in the chat; /quit leaves it. After the merge, the docs say the model is in the merged folder under your output_dir: outputs/lora-out/merged.
Tip:
--gradiogives a browser test page, but think first. The 0.21.1 source launches it withshare=cfg.get("gradio_share", True), which asks Gradio for a public share link by default (the setting is in the source, not the config reference). Addgradio_share: falseto your YAML, or stay with--chat.
Step 7 (optional): Export to GGUF and run it in Ollama#
GGUF is the file format for Ollama, llama.cpp and LM Studio. axolotl export makes it from a merged model and refuses an adapter-only folder; the docs list a standalone LoRA-to-GGUF export as a known gap (open issue #3998).
Export calls a llama.cpp checkout that is not part of Axolotl. A plain f16 conversion needs only the cloned files; quantized outputs such as Q4_K_M need a cmake build, so start without them. Do not run pip install -r on llama.cpp's requirements in your training setup: the docs warn it would replace the PyTorch and transformers versions Axolotl needs.
Linux#
docker compose run --rm axolotl bash -c "git clone https://github.com/ggml-org/llama.cpp /workspace/axolotl/outputs/llama.cpp && LLAMA_CPP_DIR=/workspace/axolotl/outputs/llama.cpp axolotl export configs/my_training.yml"
Windows (PowerShell)#
docker compose run --rm axolotl bash -c "git clone https://github.com/ggml-org/llama.cpp /workspace/axolotl/outputs/llama.cpp && LLAMA_CPP_DIR=/workspace/axolotl/outputs/llama.cpp axolotl export configs/my_training.yml"
Mac (Terminal)#
git clone https://github.com/ggml-org/llama.cpp ../llama.cpp export LLAMA_CPP_DIR=$PWD/../llama.cpp axolotl export configs/my_training.yml
The file lands in outputs/lora-out/gguf/ as <run>-f16.gguf (the docs' default pattern). Ollama's import docs need a Modelfile with one line, then one command:
FROM /full/path/to/the-file.gguf
ollama create my-model ollama run my-model
For Ollama itself see our Ollama guide.
Check it worked. ollama run my-model answers. If it ignores your format, check for a missing chat template: the docs call it only a warning, but the runtime may then use a default template and undo your fine-tune.
Put it behind HTTPS (with or without Traefik)#
Axolotl has no web page, so most readers can skip this section. It matters only for the optional cloud image axolotlai/axolotl-cloud, which starts JupyterLab on port 8888 (JUPYTER_DISABLE=1 turns it off). In the 0.21.1 entrypoint JupyterLab runs as root on all addresses, and JUPYTER_PASSWORD becomes its login token. It is a full shell: prefer an SSH tunnel, or use HTTPS and a long token.
Put JUPYTER_PASSWORD=<a-long-random-value> in .env (chmod 600 .env). The cloud image keeps data in /workspace/data:
services:
axolotl-cloud:
image: axolotlai/axolotl-cloud:0.21.1-py3.12-cu130-2.13.0
container_name: axolotl-cloud
restart: unless-stopped
ipc: host
env_file: .env
environment:
AXOLOTL_DO_NOT_TRACK: "1"
volumes:
- ./data:/workspace/data
ports:
- "127.0.0.1:8888:8888"
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
With Traefik#
On a server that already runs Traefik v3, delete the ports: block and add labels. It assumes an external network proxy and an entrypoint https. Create the network once if missing: docker network create proxy.
services:
axolotl-cloud:
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.routers.axolotl.rule=Host(`axolotl.<your-domain>`)"
- "traefik.http.routers.axolotl.entrypoints=https"
- "traefik.http.routers.axolotl.tls=true"
- "traefik.http.services.axolotl.loadbalancer.server.port=8888"
networks:
proxy:
external: true
Merge it into the file above, and add traefik.http.routers.axolotl.tls.certresolver=<your-resolver> if you use ACME. Check it worked: curl -I https://axolotl.<your-domain>/ answers over HTTPS (a redirect to the login page is normal).
Without Traefik: Caddy#
Choose Caddy if you have no Traefik. It gets and renews certificates by itself.
| Your case | What Caddy does | What you do |
|---|---|---|
| Public domain | Gets a free Let's Encrypt certificate | Point a DNS A record at the server, open ports 80 and 443 |
| Home only | Makes its own certificate with tls internal | Trust Caddy's root certificate on each device |
Save the Caddyfile as ~/docker_volumes/caddy/conf/Caddyfile. Mount the folder conf, not the single file: the Caddy image docs warn that some editors break reloads of a mounted file.
axolotl.example.com {
reverse_proxy axolotl-cloud:8888
}
Home only (a name your own DNS or hosts file points at this machine):
axolotl.home.arpa {
tls internal
reverse_proxy axolotl-cloud:8888
}
Save this as ~/docker_volumes/caddy/docker-compose.yml. In the Axolotl service remove ports: and add networks: [proxy], as in the Traefik block without the labels. Caddy passes WebSockets through by default, which JupyterLab needs.
services:
caddy:
image: caddy:2.11.8
container_name: caddy
restart: unless-stopped
ports:
- "80:80/tcp"
- "443:443/tcp"
- "443:443/udp"
volumes:
- ./conf:/etc/caddy
- ./data:/data
- ./config:/config
networks:
- proxy
networks:
proxy:
external: true
Trust the root certificate (home only). On each device, copy pki/authorities/local/root.crt from Caddy's data folder (here ~/docker_volumes/caddy/data/caddy/pki/authorities/local/root.crt) and import it:
- Linux (Debian or Ubuntu):
sudo cp root.crt /usr/local/share/ca-certificates/caddy-root.crt && sudo update-ca-certificates - Windows (PowerShell as administrator):
certutil -addstore -f ROOT root.crt - Mac:
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain root.crt
Check it worked: curl -I https://axolotl.example.com/ answers over HTTPS. Caddy is pinned to 2.11.8, the current release when this guide was checked.
Security and testing#
Keep it on your machine, keep secrets in .env, and know what the defaults send out.
- Telemetry. On by default, opt-out (PostHog). The docs say it collects system and hardware info, runtime metrics, error types and whitelisted model names, no personal data. We set
AXOLOTL_DO_NOT_TRACK=1;DO_NOT_TRACK=1also works. Without either, start-up pauses 10 seconds with a warning. - Secrets. A Hugging Face token (gated models only) goes into
.envasHF_TOKEN=<YOUR_HF_TOKEN>, never in the YAML or git. Runchmod 600 .envand add.envto.gitignore. - Public links. See the Gradio tip in Step 6. In the cloud image, SSH starts only when
PUBLIC_KEYorSSH_KEYis set. - Tests.
docker compose run --rm axolotl nvidia-smi(GPU visible),... axolotl --help(CLI runs), and the Step 3 smoke test.
Common mistakes#
Each row comes from the Axolotl docs or a real report in the Axolotl issue tracker (state as read in November 2026). The last row is about AI assistants.
| Mistake | Why it happens | Do this instead |
|---|---|---|
| The tuned model never stops talking | The end-of-turn token (<|im_end|>) is not trained when it differs from the tokenizer's end token (#4128, closed). #3754 (closed) is the same for Gemma, with healthy-looking loss | Read the start-up warning. Set eot_tokens to your template's token |
path: json plus data_files: | An old loading style; Axolotl asks Hugging Face for the file and answers 401 Client Error (#2709, closed) | path: configs/my_data.jsonl |
Keeping processor_type: AutoProcessor in a text-only config | A leftover from a multimodal recipe; KeyError: 0 while preprocessing, fixed by removing it (#3655, closed) | Remove it from text-only configs |
pip install axolotl[flash-attn,deepspeed] on plain Windows | Dependencies like triton have no matching Windows package (#2810, closed) | WSL2 or Docker, as the docs recommend |
Installing flash-attn by hand and hitting undefined symbol | A flash-attn build that does not match the PyTorch and CUDA in use (#3142, closed); a maintainer pointed to the Docker images | Use this guide's image; do not mix in your own flash-attn |
sample_packing plus pad_to_sequence_len and loss 0 | One Qwen3.5 run showed loss 0 until both were off (#3453). A maintainer asked for a start from an example config | Start from the example; change one option per run |
exitcode: -9 | The FAQ: usually the machine ran out of system RAM, not GPU memory | Close other programs, add RAM or swap, or shrink batch settings |
| A CUDA 13 image on an old driver | The images use CUDA 13.0; NVIDIA says CUDA 13.x needs driver 580+ | Run nvidia-smi first; update the driver |
| Pasting an old recipe from an AI assistant | Assistants copy old posts: type: sharegpt (docs: use chat_template), accelerate launch -m axolotl.cli.train (docs: axolotl train), path: json with data_files (dropped, per #2709). Our assessment from the docs, not a measured test | Check each key against docs.axolotl.ai. axolotl config-schema and axolotl agent-docs sft give an assistant the real schema from your install |
Tip: Change one thing at a time. When a run breaks, you want to know which line did it.
Tip: A falling loss does not prove the model stops, follows your format or knows your facts. Chat with it (Step 6) before you trust it.
Troubleshooting#
| Problem | Solution |
|---|---|
| Container cannot see the GPU | Re-run docker run --rm --gpus all ubuntu nvidia-smi. Linux: install the NVIDIA Container Toolkit and restart Docker. Windows: WSL 2 engine and wsl --update |
| CUDA out of memory | Lower micro_batch_size or sequence_len, keep gradient_checkpointing: true, or use QLoRA (load_in_4bit: true, adapter: qlora) |
| Merge size mismatch when loading adapters | Use axolotl merge-lora, not your own script (FAQ: vocabulary size can differ) |
Cannot edit files in outputs (Linux) | They are root-owned: sudo chown -R $USER ~/docker_volumes/axolotl/outputs |
Exact error texts#
ValueError: Asking to pad but the tokenizer does not have a padding token. Please select a token to use as pad_token. From the FAQ. Add special_tokens: with pad_token: (if unsure, the same value as eos_token). The example uses "<|end_of_text|>".
Data processing error: CAS service error. From the FAQ. Set HF_HUB_DISABLE_XET=1 under environment: in the Compose file.
chat_template choice is tokenizer_default but tokenizer's chat_template is null. The tokenizer has no chat template. Set chat_template: chatml (or another supported name).
ImportError: ... flash_attn_2_cuda ... undefined symbol. Issue #3142. Use the Docker image.
Known bugs (open in November 2026)#
- #4119 (October 2026): with some templates (Phi-3.5, Phi-4), a trained turn that starts with a space also trains the role-header token.
- #3998: a LoRA adapter cannot be exported to a standalone GGUF yet; merge first.
- #2396 (March 2025): tokenizing a dataset of very long multi-turn chats gets very slow near the end.
Updates and backups#
Update. Change the tag in docker-compose.yml, read the release notes, then pull:
docker compose pull
docker compose pull
The project moves fast: test one run, then keep the new tag.
Back up configs and outputs (the huggingface cache downloads again). Linux and Mac:
cd ~/docker_volumes/axolotl tar -czf backups/axolotl-$(date +%F).tar.gz --exclude=outputs/llama.cpp configs outputs
Windows PowerShell:
Set-Location "$HOME\docker_volumes\axolotl" tar -czf "backups\axolotl-$(Get-Date -Format yyyy-MM-dd).tar.gz" --exclude=outputs/llama.cpp configs outputs
Schedule it with cron at 03:00 on Linux and Mac (0 3 * * * cd ~/docker_volumes/axolotl && tar -czf backups/axolotl-$(date +\%F).tar.gz --exclude=outputs/llama.cpp configs outputs), or with Windows Task Scheduler.
Restore test. Unpack into a scratch folder and look for the adapter:
mkdir -p /tmp/axolotl-restore && tar -xzf backups/axolotl-<date>.tar.gz -C /tmp/axolotl-restore ls /tmp/axolotl-restore/outputs/lora-out
New-Item -ItemType Directory -Force "$HOME\axolotl-restore" | Out-Null tar -xzf "backups\axolotl-<date>.tar.gz" -C "$HOME\axolotl-restore" Get-ChildItem "$HOME\axolotl-restore\outputs\lora-out"
If adapter_model.safetensors is there, the backup works.
Performance and resources#
Model size and method decide the memory; context length and batch size decide how much extra. This is the docs' own approximate table for a short context (512-2048 tokens) and a micro batch of 1-2:
| Model size | QLoRA (4-bit) | LoRA (bf16) | Full (bf16 + AdamW) |
|---|---|---|---|
| 1-3B | 6-8 GB | 8-12 GB | 24-32 GB |
| 7-8B | 10-14 GB | 16-24 GB | 60-80 GB |
| 13-14B | 16-20 GB | 28-40 GB | 120+ GB |
| 30-34B | 24-32 GB | 64-80 GB | 2-4x 80 GB |
| 70-72B | 40-48 GB | 2x 80 GB | 4-8x 80 GB |
- QLoRA keeps model memory at about 25 percent of full, with slower steps and a slight quality loss. LoRA uses 50-100 percent and is close to full quality for most tasks. Full updates every weight.
- Several GPUs are needed for full tuning of 7B and up, and for LoRA of 30B and up (docs).
- Knobs. Lower
micro_batch_sizeandsequence_lenfirst;gradient_checkpointingtrades about 30 percent speed for memory. - Monitor.
nvidia-smi -l 2in a second terminal, anddocker statsfor CPU and RAM. The log'smemory/max_*figures differ fromnvidia-smibecause they come from PyTorch's counters (FAQ). - Not measured here. We took no timings.
FAQ#
Is Axolotl still maintained? Yes. Release 0.21.1 is from October 2026, the README lists changes for October 2026, and the tracker is active. The project lives at axolotl-ai-cloud; the older OpenAccess-AI-Collective address redirects.
Can I train on a Mac or an AMD GPU? Mac: partly, LoRA and full training only (Step 1). AMD: the README names AMD GPUs as supported; this guide covers NVIDIA, so follow the docs' AMD page.
How much data do I need? The Axolotl docs give no number. Start with a few hundred good examples and check the output by hand.
Does Axolotl send my data anywhere? Training stays local. Telemetry is on by default and off with AXOLOTL_DO_NOT_TRACK=1. The Gradio test page may create a public link by default (Step 6).
Sources#
- Axolotl documentation and README (Apache 2.0), release v0.21.1, PyPI - read November 2026
- Docs pages: Quickstart, Installation, Docker, method choice and VRAM, dataset formats, dataset loading, CLI, config reference, inference and merging, GGUF export, Mac, telemetry, FAQ - read November 2026
- Source at tag v0.21.1: inference.py, cloud-entrypoint.sh, lora-1b.yml; Docker Hub - read November 2026
- Issues in axolotl-ai-cloud/axolotl: #4128, #3754, #4119, #3655, #3453, #3142, #2810, #2709, #3998, #2396 - read November 2026
- NVIDIA Container Toolkit install, sample workload, CUDA release notes, Docker Desktop GPU, uv install, Hugging Face Hub variables, Ollama import, Caddy image and Caddy docs - read November 2026
- Our guides: Unsloth, Ollama, Which local LLM fits your GPU, Qdrant and RAG
Checked against the official docs on 2026-11-05.