LLMAI

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.

Axolotl official logo

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 versionAxolotl 0.21.1 (GitHub release and PyPI, October 2026)
Imageaxolotlai/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
LicenceApache 2.0
Time neededAbout 45 minutes plus downloads (our estimate; the docs give no run time)
MemoryFor a 1-3B model, the docs say 6-8 GB (QLoRA) or 8-12 GB (LoRA). Full table under "Performance and resources"
PortsNone for training. The optional cloud image runs JupyterLab on 8888
Official docsdocs.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-cloud GitHub organisation; the old OpenAccess-AI-Collective address 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.

Flow diagram of Axolotl: you write one YAML config and a small dataset, axolotl train downloads the base model from Hugging Face and trains a LoRA adapter on your GPU in a container, sends anonymous telemetry unless turned off, saves the adapter to the outputs folder, then you can chat with it, merge it into the base model and optionally export a GGUF file

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#

SystemWhat you need
LinuxNVIDIA 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
WindowsNVIDIA GPU, a current NVIDIA driver, Docker Desktop with the WSL 2 engine. The docs: "We recommend using WSL2 (Windows Subsystem for Linux) or Docker"
MacNo NVIDIA or AMD GPU and no Docker GPU. Native install only, with limits (Step 1)
Run in terminalbash
docker --version
docker compose version
nvidia-smi
  • [ ] nvidia-smi shows 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):

Run in terminalbash
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)#

  1. Install the latest NVIDIA Windows driver.
  2. Install Docker Desktop and update WSL:
Run in terminalpowershell
wsl --update
  1. 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.
Run in terminalpowershell
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:

Run in terminalbash
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-smi before 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#

Run in terminalbash
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)#

Codepowershell
$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:

File contentyaml
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:

Run in terminalbash
docker compose pull
docker compose run --rm axolotl nvidia-smi
docker compose run --rm axolotl axolotl --help
Run in terminalpowershell
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 outputs belong 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#

Run in terminalbash
docker compose run --rm axolotl bash -c "axolotl fetch examples && axolotl train examples/llama-3/lora-1b.yml"

Windows (PowerShell)#

Run in terminalpowershell
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#

Run in terminalbash
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:

Codepowershell
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:

File contentyaml
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|>"
SettingWhat it does
adapter: loraTrains 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_lenLongest example in tokens. The default is 512
micro_batch_sizeExamples per step. Lower it when memory runs out
attn_implementationflash_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_tokenThis 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 use path: json with data_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 dataUseConfig
Already tokenized (input_ids, attention_mask, labels)Pre-tokenizedtype: left empty
segments of text and label you mark yourselfTemplate-freetype: input_output
Chats: {"messages": [{"role": "...", "content": "..."}]}Conversationtype: chat_template
Instruction and answer pairsInstructiontype: alpaca and other named formats
Raw text for continued pre-trainingPre-trainingtype: 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:

File contentyaml
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#

Run in terminalbash
cd ~/docker_volumes/axolotl
docker compose run --rm axolotl axolotl train configs/my_training.yml

Windows (PowerShell)#

Codepowershell
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:

Run in terminalbash
axolotl train configs/my_training.yml

Check it worked. The run ends without an error and the output folder holds the adapter:

Run in terminalbash
ls outputs/lora-out
Codepowershell
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: 30 to the YAML. The config reference says it wins over num_epochs.

Step 6: Test the model and merge the adapter#

The same YAML is used for inference and merging (the Quickstart says so).

Linux#

Run in terminalbash
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)#

Run in terminalpowershell
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:

Run in terminalbash
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: --gradio gives a browser test page, but think first. The 0.21.1 source launches it with share=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). Add gradio_share: false to 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#

Run in terminalbash
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)#

Run in terminalpowershell
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)#

Run in terminalbash
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:

Codetext
FROM /full/path/to/the-file.gguf
Run in terminalbash
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:

File contentyaml
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.

File contentyaml
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 caseWhat Caddy doesWhat you do
Public domainGets a free Let's Encrypt certificatePoint a DNS A record at the server, open ports 80 and 443
Home onlyMakes its own certificate with tls internalTrust 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.

File contenttext
axolotl.example.com {
    reverse_proxy axolotl-cloud:8888
}

Home only (a name your own DNS or hosts file points at this machine):

File contenttext
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.

File contentyaml
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=1 also works. Without either, start-up pauses 10 seconds with a warning.
  • Secrets. A Hugging Face token (gated models only) goes into .env as HF_TOKEN=<YOUR_HF_TOKEN>, never in the YAML or git. Run chmod 600 .env and add .env to .gitignore.
  • Public links. See the Gradio tip in Step 6. In the cloud image, SSH starts only when PUBLIC_KEY or SSH_KEY is 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.

MistakeWhy it happensDo this instead
The tuned model never stops talkingThe 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 lossRead 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 configA 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 WindowsDependencies like triton have no matching Windows package (#2810, closed)WSL2 or Docker, as the docs recommend
Installing flash-attn by hand and hitting undefined symbolA flash-attn build that does not match the PyTorch and CUDA in use (#3142, closed); a maintainer pointed to the Docker imagesUse this guide's image; do not mix in your own flash-attn
sample_packing plus pad_to_sequence_len and loss 0One Qwen3.5 run showed loss 0 until both were off (#3453). A maintainer asked for a start from an example configStart from the example; change one option per run
exitcode: -9The FAQ: usually the machine ran out of system RAM, not GPU memoryClose other programs, add RAM or swap, or shrink batch settings
A CUDA 13 image on an old driverThe 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 assistantAssistants 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 testCheck 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#

ProblemSolution
Container cannot see the GPURe-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 memoryLower micro_batch_size or sequence_len, keep gradient_checkpointing: true, or use QLoRA (load_in_4bit: true, adapter: qlora)
Merge size mismatch when loading adaptersUse 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:

Run in terminalbash
docker compose pull
Run in terminalpowershell
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:

Run in terminalbash
cd ~/docker_volumes/axolotl
tar -czf backups/axolotl-$(date +%F).tar.gz --exclude=outputs/llama.cpp configs outputs

Windows PowerShell:

Codepowershell
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:

Run in terminalbash
mkdir -p /tmp/axolotl-restore && tar -xzf backups/axolotl-<date>.tar.gz -C /tmp/axolotl-restore
ls /tmp/axolotl-restore/outputs/lora-out
Codepowershell
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 sizeQLoRA (4-bit)LoRA (bf16)Full (bf16 + AdamW)
1-3B6-8 GB8-12 GB24-32 GB
7-8B10-14 GB16-24 GB60-80 GB
13-14B16-20 GB28-40 GB120+ GB
30-34B24-32 GB64-80 GB2-4x 80 GB
70-72B40-48 GB2x 80 GB4-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_size and sequence_len first; gradient_checkpointing trades about 30 percent speed for memory.
  • Monitor. nvidia-smi -l 2 in a second terminal, and docker stats for CPU and RAM. The log's memory/max_* figures differ from nvidia-smi because 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#

Checked against the official docs on 2026-11-05.