Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

JSON vs YAML vs TOML for Config

By Kokil Thapa | Last reviewed: September 2026

Choosing between JSON vs YAML vs TOML for config is not a style debate. The wrong format slows onboarding, breaks CI pipelines, and creates silent production bugs. On real client projects I maintain Laravel apps, GitLab CI pipelines, and server configs side by side. Each format has a home. This guide compares syntax, comments, typing, parser behaviour, and tooling so you can pick one format per layer and stick with it. Start with our JSON formatter tool if you need to validate payloads before they hit production.

What is the difference between JSON, YAML, and TOML for configuration?

All three are text-based serialization formats. They store structured data that programs read at startup or during deployment. The differences sit in syntax rules, comment support, duplicate-key handling, and how forgiving parsers are.

JSON (JavaScript Object Notation) is the strictest. It maps cleanly to JavaScript objects and PHP arrays. YAML (YAML Ain't Markup Language) adds indentation-based blocks and anchors. TOML (Tom's Obvious Minimal Language) uses explicit tables and key paths with a focus on readability.

JSON vs YAML vs TOML for ConfigJSONAPIs & responsespackage-lock filesYAMLCI/CD pipelinesK8s manifestsTOMLApp project filesTooling manifestsRuntime layerPHP 8.3+ / Laravel 12 / Node.js 26 LTS.env key=value — secrets, never committed
JSON vs YAML vs TOML for config: each format maps to a different layer in a typical PHP or Laravel stack.

In practice I treat config as three layers. Environment secrets live in .env files. Application structure lives in PHP arrays or TOML. Inter-service contracts stay in JSON. CI and infrastructure stay in YAML. Mixing formats within one layer creates confusion. Keeping layers separate makes upgrades predictable.

CriteriaJSONYAMLTOML
CommentsNoYes (#)Yes (#)
Trailing commasNot allowedAllowed in YAML 1.2Allowed
Human readabilityModerateHigh (with risk)Very high
StrictnessStrictParser-dependentStrict spec
Native PHP supportjson_decode()Needs libraryNeeds library
Typical useAPIs, lock filesCI, K8s, Ansiblepyproject, Cargo, tool config
Duplicate keysLast wins (RFC)Last wins (usually)Error in spec
Multiline stringsEscaped onlyBlock scalarsTriple quotes

That table is the short answer most teams need. The rest of this article explains where each format wins and where it creates pain.

When should you use JSON for configuration files?

JSON is the default interchange format between systems. REST APIs return JSON. Browser fetch() calls expect JSON. npm lock files and Composer lock files are JSON. If a config file is generated by a tool and consumed by another tool, JSON is usually the safest pick.

Strengths of JSON config

  • Universal parser support in PHP 8.3+, JavaScript, Python, Go, and Rust.
  • Schema validation via JSON Schema works well for contract testing.
  • No indentation ambiguity — whitespace is insignificant.
  • Diffs in Git are predictable when keys stay sorted.

JSON config example

{
  "app": {
    "name": "Booking Portal",
    "debug": false,
    "timezone": "Asia/Kathmandu"
  },
  "database": {
    "driver": "mysql",
    "port": 3306
  }
}

PHP reads this with one line:

$config = json_decode(
    file_get_contents(config_path('app.json')),
    true,
    512,
    JSON_THROW_ON_ERROR
);

On production Laravel applications I use JSON for export/import payloads and third-party webhook bodies. I rarely hand-edit JSON config because missing commas cause hard failures. For large payloads, see our guide on PHP JSON handling for large payloads.

JSON also powers structured LLM outputs. Teams building AI features often require JSON mode from providers. That pattern is covered in structured outputs and JSON mode from LLMs.

When should you use YAML for configuration files?

YAML dominates DevOps tooling. GitLab CI, GitHub Actions, Kubernetes, Docker Compose, and Ansible all expect YAML. If your config ships inside a pipeline or a cluster manifest, YAML is often mandatory — not optional.

Why ops teams prefer YAML

YAML supports comments. That matters when six months pass and nobody remembers why a timeout was set to 90 seconds. Block scalars handle multiline shell scripts without escape-character soup. Anchors and aliases reduce duplication across large manifests.

Those anchors are also YAML's biggest footgun. A typo in an alias reference fails at runtime, not at commit time. Our dedicated article on YAML anchors, aliases, and gotchas walks through real failure cases.

GitLab CI YAML example

stages:
  - test
  - deploy

phpunit:
  stage: test
  script:
    - composer install --no-dev --prefer-dist
    - php artisan test

deploy_production:
  stage: deploy
  script:
    - dep deploy production
  only:
    - main

I run Deployer 7 pipelines on several sister sites with this exact pattern. The YAML lives in .gitlab-ci.yml. Secrets come from CI variables — never from the file itself. For a deeper walkthrough, read the GitLab CI YAML deep dive for PHP projects.

Which Config Format?Who edits this file?Machine onlyUse JSONOps / CI teamUse YAMLDevelopersUse TOMLNever put secrets in any of theseUse .env or a secret managerRotate keys after staff changesValidate YAML in CI before merge
Decision flow for JSON vs YAML vs TOML for config: match the format to who maintains the file.

Kubernetes and Terraform-adjacent workflows reinforce YAML's dominance. If you compare config management tools, our article on Ansible vs Terraform config vs provisioning shows where YAML sits in the stack.

When should you use TOML for configuration files?

TOML was designed for config files that humans edit daily. Rust (Cargo.toml), Python (pyproject.toml), and Hugo (hugo.toml) standardised on it. The syntax is explicit. Tables are labelled with [section] headers. Nested tables use dotted paths or sub-table headers.

Why developers like TOML

TOML rejects ambiguous typing less often than YAML. The string "yes" stays a string. The boolean true stays boolean. YAML 1.1 treated yes/no as booleans — a surprise that still breaks legacy configs. TOML's spec at toml.io is short and readable.

TOML config example

[app]
name = "Nepal Gift Card"
debug = false
timezone = "Asia/Kathmandu"

[database]
driver = "mysql"
port = 3306

[features]
gift_wrapping = true
multi_currency = ["NPR", "USD", "AUD"]

PHP has no built-in TOML parser. You add a Composer package such as yosymfony/toml or devsisters/toml. That extra dependency is acceptable when TOML is the ecosystem standard for your stack. For pure PHP/Laravel projects, native PHP config arrays in config/ often beat importing TOML.

Symfony projects sometimes mix YAML and PHP config per environment. See Symfony environment config for multi-environment apps for that pattern on PHP 8.4+ with Symfony 8.1.

How do JSON, YAML, and TOML compare in a Laravel or PHP project?

Laravel 12 ships with PHP config files in config/. Values pull from .env via env(). This is not JSON, YAML, or TOML — but it solves the same problem with better IDE support and opcache caching.

I have upgraded Laravel apps across six major versions. PHP config arrays survive upgrades better than external format migrations. When a client asks to "use YAML for everything," I push back unless a specific tool requires it.

Laravel config pattern

<?php
// config/services.php
return [
    'khalti' => [
        'public_key' => env('KHALTI_PUBLIC_KEY'),
        'secret_key' => env('KHALTI_SECRET_KEY'),
        'sandbox'    => env('KHALTI_SANDBOX', true),
    ],
];

Frontend tooling in Laravel uses yet another format. Vite 8.x reads vite.config.js — JavaScript, not JSON. Our Vite config for Laravel projects guide explains that layer. Laravel caching of config, routes, and views is covered in Laravel caching strategies.

Same Config, Three SyntaxesJSON{ "db": {"port": 3306} }No commentsStrict commasYAMLdb:port: 3306# comment okIndent mattersType coercion riskTOML[db]port = 3306# comment okExplicit typesTable sectionsPick one format per layer — do not mix in one file
Syntax comparison for JSON vs YAML vs TOML for config: identical data, different rules and failure modes.

Database JSON columns vs config files

Do not confuse config file formats with database JSON columns. MySQL 9.7 and PostgreSQL 18 both store JSON in columns for flexible schemas. That is runtime data, not static config. Our comparison of MySQL vs PostgreSQL JSON handling covers query patterns — not file formats.

WordPress and WooCommerce 11.1 context

WordPress stores most settings in MySQL, not flat files. WooCommerce exports sometimes use JSON or CSV. Plugin config occasionally ships as JSON in the plugin directory. For WordPress-specific builds, see WordPress development services.

What are common mistakes when picking a config format?

These mistakes recur on client projects and production deployments. Most are preventable with a short team convention document.

  1. Storing secrets in committed config files. API keys in config.yml end up in Git history forever. Use .env, GitLab masked variables, or AWS Parameter Store.
  2. Using YAML 1.1 parsers for Kubernetes. Tools expect YAML 1.2 behaviour. Pin parser versions in CI. Validate with yamllint before merge.
  3. Hand-editing generated JSON. Lock files and build artefacts should be regenerated — not patched manually.
  4. Converting working PHP config to YAML for aesthetics. You gain syntax sugar and lose static analysis plus opcache benefits.
  5. Ignoring charset and tab characters in YAML. Tabs are forbidden for indentation in YAML. UTF-8 BOM bytes cause silent parse failures on Linux servers.
  6. Assuming JSON allows comments. JSONC extensions exist in editors, but standard json_decode() rejects them.

On booking platforms like Adventure Third Pole Trek, config spans Laravel PHP files, GitLab CI YAML, and JSON API payloads to payment gateways. Each format stays in its lane. That separation reduced deployment errors during peak trekking season.

Config Pipeline: Dev to ProductionDeveloperEdits YAML/PHPGit pushTriggers CICI validateyamllint + testsDeployer 7Symlink swapProduction server (Ubuntu 24 + PHP-FPM 8.3).env secrets + config/*.php arrays + opcachephp artisan config:cache after deployFailure pointStale opcache after deployFixReload PHP-FPM post-deploy
JSON vs YAML vs TOML for config in practice: YAML validates in CI, PHP config runs in production, JSON handles API contracts.

I've seen Laravel apps serve stale config because config:cache ran before the symlink swap. The fix is a Deployer hook that clears and rebuilds config after the release pointer moves. Server admin details sit in our Linux system administration services page.

Validation tooling worth adding

Add format-specific checks to CI:

# .gitlab-ci.yml excerpt
validate_config:
  stage: test
  script:
    - yamllint -d relaxed .gitlab-ci.yml
    - php -r 'json_decode(file_get_contents("composer.json"), true, 512, JSON_THROW_ON_ERROR);'
    - php artisan config:clear && php artisan config:cache

For JSON schema validation, the official RFC 8259 JSON specification defines parsing rules. For YAML, the YAML 1.2.2 specification clarifies boolean and float handling that trips up older parsers.

Dotfiles and server config in Git

Some teams version nginx, Apache, and PHP-FPM configs in Git. That workflow mixes formats — often YAML for CI and plain conf syntax for daemons. Our guide on managing dotfiles and server config with Git covers branch strategy and secret exclusion.

TypeScript and frontend config

Frontend monorepos increasingly use tsconfig.json — JSON with comments in practice, though strict JSON parsers reject them. Read TypeScript config explained for beginners for that overlap. Node.js 26 LTS projects may also ship package.json with an "type": "module" field — JSON config that controls module resolution.

Key Takeaways

  • Use JSON for API contracts, lock files, and machine-generated config — validate with JSON_THROW_ON_ERROR in PHP 8.3+.
  • Use YAML for CI/CD, Kubernetes, and Ansible — add yamllint to catch indentation errors before deploy.
  • Use TOML when your language ecosystem standardises on it — avoid adding TOML parsers to Laravel unless you have a clear reason.
  • Keep secrets in .env or a vault — never in committed JSON, YAML, or TOML files.
  • Run php artisan config:cache after deploy on Laravel production servers and reload PHP-FPM to flush opcache.
  • Document one format per config layer in your repo README so new developers do not introduce a fourth format.

People Also Ask

Is YAML a superset of JSON?

YAML 1.2 can represent any JSON value, but the syntax rules differ. Most YAML parsers accept JSON-style flow mappings ({ "key": "value" }). Do not assume all YAML tools accept all JSON extensions. JSON remains the safer choice for strict interchange.

Can Laravel use YAML config files natively?

Laravel does not ship YAML config support. You can parse YAML with a Symfony YAML component or Spatie's packages, then merge into the service container. Native PHP arrays in config/ are simpler, faster with opcache, and better supported by the ecosystem.

Which config format is best for Docker Compose?

Docker Compose files are YAML by specification. Compose v2 supports optional JSON with the --file flag, but YAML is the documented default. Use .env for secrets referenced via ${VAR} syntax in the compose file.

Does TOML replace JSON or YAML?

No. TOML targets project manifest files edited by developers — not API payloads or cluster manifests. Each format occupies a different niche. Pick based on tooling requirements, not personal preference alone.

Pick the right format and ship with confidence

The JSON vs YAML vs TOML for config decision is really a decision about who owns each file. Machines get JSON. Pipelines get YAML. Developer manifests get TOML. Laravel apps get PHP arrays plus .env. Write that convention down, validate in CI, and stop debating syntax in every pull request.

If you are standardising config across a new platform — booking portal, eCommerce build, or legal-tech site — I can help architect the stack from day one. See our custom software development services or browse the project portfolio for shipped examples. When you need to sanity-check JSON before it goes into an API payload, use the JSON formatter. For a broader read on config management, visit the blog or contact us to discuss your stack.

Frequently Asked Questions

All three are text-based serialization formats that store structured data programs read at startup or during deployment. JSON is the strictest, mapping cleanly to JavaScript objects and PHP arrays. YAML adds indentation-based blocks, anchors, and comments. TOML uses explicit table headers and key paths focused on readability. They differ in comment support, trailing commas, duplicate-key handling, multiline string syntax, and how forgiving parsers are when a file has syntax errors.

JSON is the default interchange format between systems. Use it for REST API contracts, webhook payloads, npm and Composer lock files, and any config generated by one tool and consumed by another. PHP 8.3+ reads JSON natively with json_decode() and JSON_THROW_ON_ERROR. JSON Schema validation works well for contract testing, and Git diffs stay predictable when keys stay sorted. Avoid hand-editing JSON config because missing commas cause hard failures and standard JSON does not support comments.

YAML dominates DevOps tooling. GitLab CI, GitHub Actions, Kubernetes, Docker Compose, and Ansible all expect YAML, so if your config ships inside a pipeline or cluster manifest, YAML is often mandatory. Comments document why a timeout was set to 90 seconds, block scalars handle multiline shell scripts, and anchors reduce duplication across large manifests. Add yamllint in CI to catch indentation errors. Secrets belong in CI variables or .env references, never in committed YAML files.

TOML targets config files humans edit daily. Rust Cargo.toml, Python pyproject.toml, and Hugo hugo.toml standardised on it. Tables use [section] headers, nested tables use dotted paths, and the string "yes" stays a string unlike YAML 1.1 boolean surprises. PHP has no built-in TOML parser; add yosymfony/toml or devsisters/toml via Composer. For pure Laravel projects, native PHP config arrays in config/ often beat importing TOML unless your language ecosystem already requires it.

YAML 1.2 can represent any JSON value, but syntax rules differ. Most parsers accept JSON-style flow mappings. JSON remains the safer choice for strict machine interchange.

No. Laravel does not ship YAML config support. Parse YAML via Symfony YAML or Spatie packages, or stick with native PHP arrays in config/.

Docker Compose files are YAML by specification. Compose v2 supports optional JSON with --file, but YAML is the documented default. Use .env for secrets.

No. TOML targets developer-edited project manifests like pyproject.toml and Cargo.toml, not API payloads or cluster manifests. Each format occupies a different niche. Pick based on who maintains the file and what tooling requires: machines get JSON, pipelines get YAML, developer manifests get TOML. Mixing formats within one config layer creates confusion that a short team convention document in your repo README can prevent.

Laravel 12 ships with PHP config files in config/ pulling values from .env via env(), not JSON, YAML, or TOML. That pattern offers better IDE support and opcache caching. Use JSON for export or import payloads and third-party webhook bodies. Use YAML for .gitlab-ci.yml pipeline files. Frontend tooling adds Vite 8.x reading vite.config.js, which is JavaScript, not JSON. I have upgraded Laravel apps across six major versions; PHP config arrays survive upgrades better than external format migrations.

Recurring mistakes include storing API keys in committed config files that live in Git history forever, using YAML 1.1 parsers where Kubernetes expects YAML 1.2 behaviour, hand-editing generated JSON lock files, converting working PHP config to YAML for aesthetics and losing static analysis plus opcache benefits, using tabs for YAML indentation when tabs are forbidden, and assuming JSON allows comments when standard json_decode() rejects them. UTF-8 BOM bytes in YAML can cause silent parse failures on Linux servers.

Do not store secrets in any committed config format. API keys in config.yml or app.json end up in Git history permanently. Keep environment secrets in .env files locally, GitLab masked variables or AWS Parameter Store in CI, and vault references in production. Docker Compose references secrets via ${VAR} syntax pointing to .env. Laravel config files should call env() for payment gateway credentials like Khalti keys, never hard-code them. This separation is standard on production Laravel applications I maintain with Deployer 7 and GitLab CI.

Ops teams prefer YAML because comments explain why a deploy timeout is 90 seconds six months later. Block scalars handle multiline deploy scripts without escape-character soup. GitLab CI, Kubernetes, and Ansible all expect YAML, making it mandatory in those layers rather than optional. Anchors and aliases reduce duplication across large manifests, though alias typos fail at runtime, not at commit time. Validate with yamllint in CI before merge to catch indentation errors that JSON's insignificant whitespace avoids but YAML punishes.

Add format-specific checks to your GitLab CI test stage. Run yamllint -d relaxed against .gitlab-ci.yml. Validate composer.json with php -r and JSON_THROW_ON_ERROR. On Laravel production deploys, run php artisan config:clear and php artisan config:cache after the Deployer symlink swap so stale cached config does not serve old values. Pin YAML parser versions in CI because YAML 1.1 and 1.2 handle booleans differently. JSON Schema validation helps for API contract config following RFC 8259 parsing rules.

Standard JSON does not allow comments. JSONC extensions exist in some editors, but PHP json_decode() rejects them and throws with JSON_THROW_ON_ERROR. This is why teams rarely hand-edit JSON config on production Laravel applications. If you need commented human-edited config in a PHP stack, Laravel PHP config files, YAML for CI, or TOML for ecosystem manifests are better fits. For machine-generated interchange between services, JSON strictness is an advantage because parsers behave consistently across PHP 8.3+, JavaScript, Python, Go, and Rust.

Config files store static application settings read at startup or deploy time. Database JSON columns in MySQL 9.7 or PostgreSQL 18 store runtime flexible schema data queried during requests. Do not confuse the two when architecting a stack. A Laravel booking portal might use JSON config for payment gateway webhook contracts while storing dynamic user data in database JSON columns. Config file format decisions affect CI, deployment, and developer onboarding; database JSON affects query performance, indexing, and runtime data flexibility.

Share this article

0 Comments

Leave a comment

Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

Quick Contact Options
Choose how you want to connect me: