# Snakemake Configuration Files: Centralizing Parameters

By the end of this guide you will be able to build a snakemake config file that holds every adjustable value of a workflow, load it three different ways, override any value from the command line, validate it against a schema before a long run, and sidestep the YAML traps that quietly turn sample names into numbers. You need Python 3.11 or newer, Snakemake 8 or 9 installed (commands below), a plain text editor, and optionally cutadapt, MultiQC, and the jsonschema package for the worked pipeline.

A bioinformatics workflow manager is a tool that chains analysis steps, resolves file dependencies, parallelizes work across cores or a cluster, and records what ran. Snakemake is one of the two most widely used open source options in genomics, alongside Nextflow. Its design separates two concerns: the Snakefile describes what the workflow does, and a separate configuration file holds the values that change from project to project.

## Why centralize parameters in a snakemake config file

Every bioinformatics pipeline contains two kinds of content. Logic: which tools run, in what order, on which files. Settings: which samples, which reference genome, which adapter sequences, how many threads. When settings live inside the workflow code, every new project starts with a risky edit to a file that used to work.

The Snakemake paper (Mölder and colleagues, "Sustainable data analysis with Snakemake", F1000Research, 2021) presents this separation as an explicit design goal: the workflow definition stays generic while configuration adapts it to a specific analysis. The official Snakemake documentation recommends the same pattern for its own examples.

In practice, a snakemake config file gives you four things:

1. One auditable place where every parameter lives.
2. A file you can version control, diff, and review.
3. One Snakefile shared across projects instead of a fork per project.
4. A clean interface for per-site or per-run overrides without touching code.

Values that belong in the config file: sample names, input and output paths, tool parameter values such as adapter sequences and quality cutoffs, thresholds, labels for reports. Values that belong in the Snakefile: rule structure, dependencies, and any real logic.

## Snakemake Config File Step by Step

This walkthrough builds a small but realistic RNA-seq style workflow. Every command is real and every expected output is shown, so you can follow along on a laptop.

### Step 1: Install Snakemake and lay out the project

Install with conda or mamba, which is how the Snakemake documentation recommends deploying it:

```bash
mamba create -n snakemake -c conda-forge -c bioconda snakemake
conda activate snakemake
snakemake --version
```

You should see a version string. Snakemake 8 and 9 both work for everything in this guide. A plain pip install also exists (`pip install snakemake`) if you do not use conda.

Create a project directory with this layout:

```
myproject/
    Snakefile
    config.yaml
    schema.yaml
    data/
        WT_rep1_R1.fastq.gz
        WT_rep1_R2.fastq.gz
        KO_rep1_R1.fastq.gz
        KO_rep1_R2.fastq.gz
    resources/
    results/
```

The two files that matter are `Snakefile` (the workflow logic) and `config.yaml` (the settings).

### Step 2: Write your first config file and load it three ways

Create `config.yaml` with two settings:

```yaml
# config.yaml
message: "hello from config"
repeats: 3
```

Create a minimal `Snakefile` that uses them:

```python
configfile: "config.yaml"

rule all:
    input:
        "results/hello.txt"

rule hello:
    output:
        "results/hello.txt"
    run:
        with open(output[0], "w") as out:
            out.write((config["message"] + "\n") * config["repeats"])
```

Run it:

```bash
snakemake --cores 1
```

Expected output (timestamps and log paths trimmed, exact wording varies slightly between Snakemake versions):

```
Building DAG of jobs...
Job stats:
job      count
-----  ------
all         1
hello       1
total       2

Finished job 0.
2 of 2 steps (100%) done
```

Confirm the config values actually flowed through:

```bash
cat results/hello.txt
```

```
hello from config
hello from config
hello from config
```

There are three ways to load a snakemake config file, and the example above used the first:

1. The `configfile:` directive at the top of the Snakefile.
2. The command line flag `-configfile other.yaml`, which overrides the directive.
3. Automatic detection: if you specify nothing, Snakemake uses a file named `config.yaml` in the working directory.

The `config` object is then available everywhere in the Snakefile as a plain Python dictionary.

### Step 3: See exactly what YAML hands to Python

Snakemake reads config files with PyYAML, which implements YAML 1.1. That version of YAML silently converts many bare values to other types. Before wiring a config into a workflow, look at what Python will actually receive:

```bash
python - <<'EOF'
import yaml
from pprint import pprint
with open("config.yaml") as fh:
    pprint(yaml.safe_load(fh))
EOF
```

To see the conversions clearly, test a file that contains one of each common case:

```yaml
# types.yaml
count: 8
ratio: 0.5
flag: true
sample_id: 01
adapter: no
runtime: 4:00:00
version: 2.10
```

```bash
python - <<'EOF'
import yaml
from pprint import pprint
with open("types.yaml") as fh:
    pprint(yaml.safe_load(fh))
EOF
```

Expected output:

```
{'adapter': False,
 'count': 8,
 'flag': True,
 'ratio': 0.5,
 'runtime': 14400,
 'sample_id': 1,
 'version': 2.1}
```

Read that carefully, because each line is a real bug waiting for an unprepared pipeline:

- `sample_id: 01` became the integer `1`. YAML 1.1 treats leading zero numbers as octal, so `010` would become `8`.
- `adapter: no` became the boolean `False`. In YAML 1.1, `no`, `yes`, `on`, and `off` are booleans.
- `runtime: 4:00:00` became the integer `14400`, because YAML 1.1 parses colon separated numbers as sexagesimal (base 60) integers. Four hours became 14,400 seconds.
- `version: 2.10` became the float `2.1`, losing the trailing zero.

The fix for all four is the same: put quotes around any value that must stay a string, for example `sample_id: "01"` and `runtime: "4:00:00"`.

### Step 4: Wire the config into a real workflow

Now replace the toy files with a realistic setup. This pipeline trims paired end reads with cutadapt and summarizes the logs with MultiQC. Both tools are standard, and the same wiring pattern applies to any command line tool.

`config.yaml`:

```yaml
samples:
  - WT_rep1
  - WT_rep2
  - KO_rep1
  - KO_rep2

paths:
  data: data
  results: results
  logs: results/logs

threads: 8

cutadapt:
  adapter_r1: "AGATCGGAAGAGCACACGTCTGAACTCCAGTCA"
  adapter_r2: "AGATCGGAAGAGCGTCGTGTAGGGAAAGAGTGT"
  minimum_length: 20
  quality_cutoff: 20

multiqc:
  title: "WT versus KO, batch 1"
```

The adapter strings are the standard Illumina TruSeq universal adapter sequences for read 1 and read 2. `Snakefile`:

```python
configfile: "config.yaml"

SAMPLES = config["samples"]
DATA = config["paths"]["data"]
RESULTS = config["paths"]["results"]
LOGS = config["paths"]["logs"]

rule all:
    input:
        expand(f"{RESULTS}/trimmed/{{sample}}_1.trimmed.fastq.gz", sample=SAMPLES),
        f"{RESULTS}/qc/multiqc_report.html"

rule cutadapt:
    input:
        r1=f"{DATA}/{{sample}}_R1.fastq.gz",
        r2=f"{DATA}/{{sample}}_R2.fastq.gz",
    output:
        r1=f"{RESULTS}/trimmed/{{sample}}_1.trimmed.fastq.gz",
        r2=f"{RESULTS}/trimmed/{{sample}}_2.trimmed.fastq.gz",
        log=f"{LOGS}/{{sample}}.cutadapt.log",
    params:
        a1=config["cutadapt"]["adapter_r1"],
        a2=config["cutadapt"]["adapter_r2"],
        m=config["cutadapt"]["minimum_length"],
        q=config["cutadapt"]["quality_cutoff"],
    threads: config["threads"]
    shell:
        "cutadapt"
        " -a {params.a1}"
        " -A {params.a2}"
        " -m {params.m}"
        " -q {params.q}"
        " -o {output.r1}"
        " -p {output.r2}"
        " {input.r1} {input.r2}"
        " > {output.log} 2>&1"

rule multiqc:
    input:
        expand(f"{LOGS}/{{sample}}.cutadapt.log", sample=SAMPLES)
    output:
        html=f"{RESULTS}/qc/multiqc_report.html"
    params:
        title=config["multiqc"]["title"]
    shell:
        "multiqc {input} --title '{params.title}'"
        " --outdir {RESULTS}/qc"
        " --filename multiqc_report.html"
```

Check the plan before running anything. A dry run (`-n`) builds the full job graph and prints what would execute without executing it:

```bash
snakemake -n --cores 8
```

Expected output:

```
Building DAG of jobs...
Job stats:
job        count
--------  -----
all            1
cutadapt       4
multiqc        1
total          6

This was a dry-run (flag -n). Snakemake will not execute anything.
```

Four samples produced four cutadapt jobs and one MultiQC job. The DAG matches the config. To run for real, drop the `-n`.

### Step 5: Override values from the command line

The point of centralizing parameters is to change them without editing files. The `-config` flag sets values that win over every config file:

```bash
snakemake -n --cores 8 --config threads=4
snakemake -n --cores 8 --config 'samples=[WT_rep1, KO_rep1]'
snakemake -n --cores 8 --config quality_cutoff=30
```

With the samples override, the same dry run reports `cutadapt 2` and `total 4` in the job stats, because the sample list came from the command line instead of the file.

Three behaviors matter here:

1. Snakemake parses `-config` values as YAML, so `threads=4` arrives as the integer 4 and `samples=[WT_rep1, KO_rep1]` arrives as a list. Quote values that must stay strings.
2. `-config` sets top level keys. Passing a whole nested block replaces that block, it does not merge into it.
3. For nested overrides, use a small override file instead: `snakemake, configfile base.yaml, configfile siteB.yaml`. Later files override earlier ones for shared keys, and files given on the command line override the file named in the Snakefile.

### Step 6: Validate the config before a long run

A typo in a config file costs nothing on a laptop and costs twelve hours on a cluster. Catch it first with JSON Schema validation. JSON Schema is a standard vocabulary for describing the structure of data. Add this to the top of the Snakefile, right after the `configfile:` directive:

```python
import jsonschema

SCHEMA = {
    "type": "object",
    "properties": {
        "samples": {"type": "array", "items": {"type": "string"}, "minItems": 1},
        "paths": {
            "type": "object",
            "properties": {
                "data": {"type": "string"},
                "results": {"type": "string"},
                "logs": {"type": "string"},
            },
            "required": ["data", "results", "logs"],
        },
        "threads": {"type": "integer", "minimum": 1},
        "cutadapt": {
            "type": "object",
            "properties": {
                "adapter_r1": {"type": "string"},
                "adapter_r2": {"type": "string"},
                "minimum_length": {"type": "integer", "minimum": 1},
                "quality_cutoff": {"type": "integer", "minimum": 0},
            },
            "required": ["adapter_r1", "adapter_r2", "minimum_length", "quality_cutoff"],
        },
    },
    "required": ["samples", "paths", "threads", "cutadapt"],
}

jsonschema.validate(config, SCHEMA)
```

Install the validator with `pip install jsonschema` (or conda). If a sample id lost its quotes and became a number, the workflow refuses to parse and you see the exact problem:

```
jsonschema.exceptions.ValidationError: 1 is not of type 'string'

Failed validating 'type' in schema['properties']['samples']['items']:
    {'type': 'string'}

On instance[0]:
    1
```

The Snakemake team also maintains a dedicated command line tool for this, `snakemake-schema`, which validates a config against a schema file without embedding the check in the Snakefile:

```bash
pip install snakemake-schema
snakemake_schema validate --schema schema.yaml --config-file config.yaml
```

A related hygiene check is `snakemake, lint`, which reviews the Snakefile itself for style problems that often hide config bugs, such as values hardcoded where they should come from the config.

### Step 7: Record the configuration with your results

Six months from now, the question will not be whether the pipeline ran, it will be with which settings. Make the config part of the output:

```python
rule record_config:
    input:
        "config.yaml"
    output:
        f"{RESULTS}/config_used.yaml"
    shell:
        "cp {input} {output}"
```

Add `f"{RESULTS}/config_used.yaml"` to the inputs of `rule all` so the copy always happens. For a richer record, `snakemake, report report.html` produces a self contained HTML report that embeds the parameters and provenance of every job. Committing `config.yaml` to git and tagging the commit in your [lab notebook](/knowledge/molecular-biology/lab-notebook) covers the rest.

## How Snakemake Configuration Files Load and Override Each Other

![Flow of config sources from weakest to strongest: auto-detected file, configfile directive, configfile flag, config flag, with profile kept separate.](/article-images/illustrations/snakemake-config-file-config-load-order.webp)

Snakemake configuration files can come from several places at once. When they do, this is the resolution order, from weakest to strongest:

| Mechanism | Where it lives | Example | Notes |
|--|--|--|--|
| Auto-detected file | `config.yaml` in the working directory | none needed | used when nothing else is specified |
| `configfile:` directive | Snakefile | `configfile: "config.yaml"` | loaded while the workflow parses |
| `-configfile` flag | command line | `snakemake, configfile siteB.yaml` | overrides the directive, can be repeated |
| `-config` flag | command line | `snakemake, config threads=16` | wins over every file, values parsed as YAML |
| Profile config | profile directory | `-profile slurm` | configures Snakemake itself, not the analysis |
| `config.get()` | Snakefile | `config.get("threads", 4)` | supplies a default for optional keys |

Two merging rules cover the edge cases. First, when the same key appears in more than one source, the later source replaces it entirely. Second, a dict value passed through `-config` replaces the whole subtree under that key rather than merging leaf by leaf, which is why per-key override files are the safer tool for nested settings.

One distinction prevents a common confusion. The workflow config file describes the analysis: samples, paths, tool parameters. A profile is a directory containing its own `config.yaml` that sets Snakemake's execution options, such as default cores, scheduler settings, and resource assignments. You activate one with `-profile name`. Keeping the two apart means the same workflow config runs unchanged on a laptop and on a cluster.

## How Config Values Reach Your Rules

The `config` dictionary exists from the moment the Snakefile starts parsing, and top level statements like `SAMPLES = config["samples"]` run once at parse time. Rule attributes such as `params`, `threads`, and `resources` are evaluated when the job graph is built, which is why they can reference `config` directly.

Route every config value you use in a shell command through `params`, as the walkthrough does. The official Snakemake documentation recommends this pattern for two reasons. Parameters become part of the recorded provenance of each job. And modern Snakemake reruns jobs when their parameters change, so editing `quality_cutoff` in the config correctly triggers a rerun of the affected rules on the next invocation, while a value interpolated straight into a shell string can slip past that check.

Per-sample settings need one extra pattern. Change `samples` from a list to a mapping, then read the per-sample value with a lambda in `params`:

```yaml
sample_defaults: &defaults
  length_required: 50

samples:
  WT_rep1:
    <<: *defaults
  KO_rep1:
    <<: *defaults
    length_required: 30
```

```python
rule trim:
    input:
        f"{DATA}/{{sample}}_R1.fastq.gz"
    output:
        f"{RESULTS}/trimmed/{{sample}}_R1.fastq.gz"
    params:
        length=lambda wc: config["samples"][wc.sample]["length_required"]
    shell:
        "seqkit seq -m {params.length} {input} > {output}"
```

The `&defaults` anchor and `<<: *defaults` merge key are standard YAML, supported by the loader Snakemake uses. Each sample inherits the defaults and overrides only what differs. The lambda receives the wildcard values for the concrete job, so `WT_rep1` gets 50 and `KO_rep1` gets 30.

When sample metadata grows beyond a handful of keys, move it out of YAML into a table and load it at parse time. Reading with an explicit string dtype protects ids that start with zero:

```python
import pandas as pd

units = pd.read_csv(config["samples_table"], sep="\t", dtype=str)
SAMPLES = units["sample"].unique().tolist()
```

## Snakemake Config File Tips

1. Keep the config declarative. If a value needs computation, compute it in the Snakefile and keep only the inputs in the config file.
2. Route every shell-facing value through `params` so changes trigger reruns and appear in provenance.
3. Use `config.get("key", default)` for optional settings so a missing key yields a documented default instead of a crash, and list those keys in your schema.
4. Quote anything that is not meant to be a number or a boolean, especially sample ids, versions, and time limits.
5. Prefer a shallow, explicit structure. `cutadapt.minimum_length` is easy to grep, a deeply nested tree is not.
6. Keep execution settings (cores, partitions, memory defaults) in a profile, not in the workflow config, so the analysis config stays portable between machines.
7. Version control the config with the workflow, and record the commit hash alongside results.
8. Start every project from a template repository that already contains a Snakefile, a config file, and a schema, rather than copying a finished project and editing it.
9. Test new settings on a two sample subset with `-config 'samples=[WT_rep1, KO_rep1]'` before committing to the full run.
10. Use YAML anchors for shared per-sample defaults so a change to one default does not require editing every sample block.

## Common Mistakes and Limitations

![Decision path for config failures: missing key, type change, nested override, or wrong relative path, each with its fix.](/article-images/illustrations/snakemake-config-file-config-triage.webp)

These are the failures that account for most lost hours with snakemake configuration files, with the exact messages and fixes.

**1. The config file was never loaded.** You reference a key and get:

```
KeyError in file "Snakefile", line 3:
SAMPLES = config["samples"]
```

The traceback points at the line that touched the missing key. Causes: no `configfile:` directive, no `-configfile` flag, or running `snakemake` from a directory where the auto-detected `config.yaml` does not exist. Fix: load the file explicitly with the directive or the flag, which also makes the intent visible to readers.

**2. Tab characters in the YAML.** YAML forbids tabs for indentation, and the error names the culprit:

```
yaml.scanner.ScannerError: while scanning for the next token
found character '\t' that cannot start any token
  in "config.yaml", line 4, column 1
```

Fix: spaces only. Configure your editor to insert spaces when the Tab key is pressed in YAML files.

**3. Inconsistent indentation.** YAML structure is indentation, so a value aligned to the wrong column breaks the file:

```
yaml.parser.ParserError: while parsing a block mapping
  in "config.yaml", line 2, column 3
did not find expected key
  in "config.yaml", line 5, column 5
```

Fix: two spaces per level, never aligned to the value above, and run the parsed dump from Step 3 after every edit.

**4. Unquoted values changed type.** The conversions in Step 3 hit real pipelines constantly. A sample id `01` becomes the integer `1` and file names stop matching. A time limit `4:00:00` becomes the integer `14400`, which a scheduler then interprets as 14,400 unknown units. A tool toggle written as `no` becomes `False`. Fix: quote every value that must remain a string, and let the schema reject integers where strings are required.

**5. Duplicate keys, silently resolved.** If `threads` appears twice in a config file, PyYAML does not error. The last value wins and nobody is told. Detect it with a strict loader, a pattern from the PyYAML documentation:

```bash
python - <<'EOF'
import yaml

class UniqueKeyLoader(yaml.SafeLoader):
    pass

def construct_mapping(loader, node, deep=False):
    loader.flatten_mapping(node)
    mapping = {}
    for key_node, value_node in node.value:
        key = loader.construct_object(key_node, deep=deep)
        if key in mapping:
            raise ValueError(f"duplicate key in config: {key}")
        mapping[key] = loader.construct_object(value_node, deep=deep)
    return mapping

UniqueKeyLoader.add_constructor(
    yaml.resolver.BaseResolver.DEFAULT_MAPPING_TAG, construct_mapping
)

with open("config.yaml") as fh:
    print(yaml.load(fh, Loader=UniqueKeyLoader))
EOF
```

Expected output when a duplicate exists:

```
ValueError: duplicate key in config: threads
```

**6. Expecting `-config` to merge nested structures.** Passing `-config cutadapt=...` with a new block replaces the entire `cutadapt` section, dropping keys you did not mention. Fix: override single top level keys with `-config`, and nested groups with a dedicated override file passed as a second `-configfile`.

**7. Relative paths resolved from the wrong directory.** Paths in the config resolve against the working directory where you invoke `snakemake`, not against the config file's location. Running `snakemake -s workflows/rnaseq/Snakefile` from elsewhere breaks every relative path. Fix: run from the project root, or store absolute paths, or anchor paths to the Snakefile with `os.path.join(workflow.basedir, config["paths"]["data"])`.

**8. Environment variables and home shortcuts are not expanded.** YAML has no variable expansion, so `reference: $HOME/ref/genome.fa` and `reference: ~/genome.fa` remain literal strings. Fix: expand them in the Snakefile with `os.path.expandvars(os.path.expanduser(config["paths"]["reference"]))`.

**9. Values interpolated directly into shell strings.** Writing `{config[cutadapt][quality_cutoff]}` inside a `shell:` block works once and then fails silently in two ways: the value is invisible to provenance, and changing it may not trigger the rerun you expect. Fix: declare it in `params` and reference `{params.q}`.

**10. Execution settings stored in the workflow config.** Cores, scheduler options, and per-rule resource assignments describe how to run Snakemake, not what the analysis is. Mixing them into the workflow config makes the config cluster specific. Fix: a profile directory with its own `config.yaml`, activated with `-profile`.

A note on limits. Schema validation confirms that a config file is well formed, it cannot confirm that the values are scientifically appropriate, and individual analyses still need domain judgment about adapters, thresholds, and reference choices.

## How to Verify Your Configuration

Verification is cheap and catches almost everything above:

1. Dump the parsed values with the PyYAML one liner from Step 3 and compare against what you intended, especially the types.
2. Dry run with `snakemake -n -p, cores 8`. The `-p` flag prints each shell command with config values already substituted, so you can read the exact cutadapt invocation before it runs.
3. Run a two sample subset first using a `-config` samples override, and inspect its outputs end to end.
4. After a full run, `snakemake, detailed-summary` lists every output with its timestamps, which confirms the run produced what the config described.
5. Change one config value, dry run again, and confirm that only the intended jobs are scheduled for rerun. If nothing reruns, a value is probably not routed through `params`.

Sensible next steps from here: adopt profiles for your cluster, browse the official snakemake-wrappers repository so common tools come pre-packaged with input, output, and parameter conventions, and add containerized execution so the config, the workflow, and the software all travel together.

## Nextflow vs Snakemake for Configuration

Because the two leading bioinformatics workflow managers solve the same problem differently, the comparison usually comes up during tool selection. Nextflow, introduced by Di Tommaso and colleagues in Nature Biotechnology in 2017, is written in Groovy and runs on the JVM. Snakemake is pure Python. Both are open source, actively developed, and used at scale, and both support profiles, CLI overrides, containers, and cluster execution.

The configuration difference is the sharpest one. A snakemake config file is data: a YAML or JSON document parsed into a dictionary, readable by any language, validatable with JSON Schema. Nextflow configuration is code: a `nextflow.config` file of Groovy that defines a `params` scope, can compute values, read environment variables, and branch on conditions. Code configuration is more powerful and more dangerous, because settings can contain logic that a reviewer must execute mentally. Data configuration is more portable and easier to audit, at the cost of expressiveness.

| Aspect | Snakemake | Nextflow |
|--|--|--|
| Implementation language | Python | Groovy on the JVM |
| Config format | YAML or JSON data file | Groovy `nextflow.config` |
| Parameter scope | `config` dictionary | `params` scope |
| CLI parameter override | `-config key=value` | `-key value` |
| Profiles | directory with `config.yaml`, `-profile name` | named profiles in `nextflow.config`, `-profile name` |
| Schema validation | JSON Schema, snakemake-schema | custom Groovy or plugin based |
| Notable ecosystem | snakemake-wrappers, conda integration | nf-core pipeline collection |

Choose based on your team and infrastructure. If your group thinks in Python and wants settings as reviewable data, Snakemake fits. If you plan to run nf-core pipelines or your institution already operates Nextflow towers and clusters, staying in that ecosystem is usually the lower risk choice. Either way, the principles in this article, centralize values, quote strings, validate structure, record the config with results, apply to both.

## Frequently Asked Questions

### Where does Snakemake look for the config file?

Snakemake automatically uses a file named `config.yaml` in the working directory when no other config file is specified. You can also name a file with the `configfile:` directive inside the Snakefile, or pass one explicitly with `-configfile`, which takes precedence over the directive.

### How do I override a config value from the command line?

Use the `-config` flag, for example `snakemake, config threads=16`. Snakemake parses these values as YAML, so numbers and lists arrive with the right types, and command line values win over every config file. For nested settings, pass a small override file with `-configfile` instead.

### Can I use JSON instead of YAML for a snakemake config file?

Yes. Snakemake reads both YAML and JSON config files. Only `config.yaml` is picked up automatically, so a JSON file must be named with the `configfile:` directive or the `-configfile` flag.

### How do I give each sample its own parameters?

Make `samples` a mapping of sample name to settings instead of a list, then read the value with a lambda in the rule's `params`, such as `length=lambda wc: config["samples"][wc.sample]["length_required"]`. YAML anchors let every sample inherit shared defaults and override only what differs.

### Why did my config value change type?

Snakemake reads YAML with PyYAML, which implements YAML 1.1, and that version converts bare `no`, `yes`, `on`, and `off` to booleans, treats leading zero numbers as octal, and reads colon separated numbers as base 60 integers. Quote any value that must stay a string and enforce the types with a JSON Schema.

### What is the difference between a workflow config file and a profile?

The workflow config file describes the analysis: samples, paths, and tool parameters. A profile is a directory with its own `config.yaml` that sets how Snakemake itself runs, such as default cores and scheduler options, and is activated with `-profile`. Keeping them separate lets the same analysis config run on a laptop and a cluster.

### How does Snakemake configuration compare with Nextflow?

A snakemake config file is a data document parsed into a dictionary, while Nextflow configuration is Groovy code in `nextflow.config` that defines a `params` scope. Both support profiles and command line overrides. Snakemake's data format is easier to audit and schema validate, Nextflow's is more expressive and comes with the nf-core ecosystem.

### How do I check which values Snakemake is actually using?

Snakemake does not print the resolved config by default, so verify it directly. Dump the parsed file with PyYAML to see the exact values and types Python will receive, then dry run with `-n -p` to read the final commands with every config value substituted.

<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "FAQPage",
  "mainEntity": [
    {
      "@type": "Question",
      "name": "Where does Snakemake look for the config file?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Snakemake automatically uses a file named config.yaml in the working directory when no other config file is specified. You can also name a file with the configfile directive inside the Snakefile, or pass one explicitly with, configfile, which takes precedence over the directive."
      }
    },
    {
      "@type": "Question",
      "name": "How do I override a config value from the command line?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Use the, config flag, for example snakemake, config threads=16. Snakemake parses these values as YAML, so numbers and lists arrive with the right types, and command line values win over every config file. For nested settings, pass a small override file with, configfile instead."
      }
    },
    {
      "@type": "Question",
      "name": "Can I use JSON instead of YAML for a snakemake config file?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Yes. Snakemake reads both YAML and JSON config files. Only config.yaml is picked up automatically, so a JSON file must be named with the configfile directive or the, configfile flag."
      }
    },
    {
      "@type": "Question",
      "name": "How do I give each sample its own parameters?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Make samples a mapping of sample name to settings instead of a list, then read the value with a lambda in the rule params, such as length=lambda wc: config[\"samples\"][wc.sample][\"length_required\"]. YAML anchors let every sample inherit shared defaults and override only what differs."
      }
    },
    {
      "@type": "Question",
      "name": "Why did my config value change type?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Snakemake reads YAML with PyYAML, which implements YAML 1.1, and that version converts bare no, yes, on, and off to booleans, treats leading zero numbers as octal, and reads colon separated numbers as base 60 integers. Quote any value that must stay a string and enforce the types with a JSON Schema."
      }
    },
    {
      "@type": "Question",
      "name": "What is the difference between a workflow config file and a profile?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "The workflow config file describes the analysis: samples, paths, and tool parameters. A profile is a directory with its own config.yaml that sets how Snakemake itself runs, such as default cores and scheduler options, and is activated with, profile. Keeping them separate lets the same analysis config run on a laptop and a cluster."
      }
    },
    {
      "@type": "Question",
      "name": "How does Snakemake configuration compare with Nextflow?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "A snakemake config file is a data document parsed into a dictionary, while Nextflow configuration is Groovy code in nextflow.config that defines a params scope. Both support profiles and command line overrides. Snakemake's data format is easier to audit and schema validate, Nextflow's is more expressive and comes with the nf-core ecosystem."
      }
    },
    {
      "@type": "Question",
      "name": "How do I check which values Snakemake is actually using?",
      "acceptedAnswer": {
        "@type": "Answer",
        "text": "Snakemake does not print the resolved config by default, so verify it directly. Dump the parsed file with PyYAML to see the exact values and types Python will receive, then dry run with -n -p to read the final commands with every config value substituted."
      }
    }
  ]
}
</script>