Start HereOrdered learning track

Safe Config Layout for Production

Learn NGINX In Action - Part 011

Layout konfigurasi NGINX production-grade: struktur direktori, include boundaries, snippet discipline, environment overlays, config review, validasi CI, dan rollback-friendly design.

11 min read2165 words
PrevNext
Lesson 11105 lesson track01–19 Start Here
#nginx#configuration#production#deployment+3 more

Part 011 — Safe Config Layout for Production

Target part ini: kamu bisa mendesain layout konfigurasi NGINX yang aman untuk production, bisa direview, bisa divalidasi di CI, bisa di-rollback, dan tidak membuat engineer berikutnya harus menebak dari mana sebuah directive berasal.

Pada tahap basic, konfigurasi NGINX sering terlihat sederhana:

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://app;
    }
}

Di production, bentuk seperti itu hampir tidak pernah berdiri sendiri. Akan ada TLS, static assets, reverse proxy, upstream pool, cache, CORS, security headers, logging, rate limit, maintenance mode, health endpoint, canary routing, dan beberapa environment. Jika semua itu dimasukkan ke satu file besar, NGINX tetap bisa berjalan, tetapi sistem operasionalnya rapuh.

Masalah utama bukan panjang file. Masalahnya adalah config topology.

Config topology menjawab:

Directive apa hidup di context mana?
Siapa pemilik file ini?
File mana yang boleh include file mana?
Apakah perubahan kecil bisa mengubah perilaku server lain?
Apakah config final bisa dibuktikan sebelum reload?
Apakah rollback bisa dilakukan tanpa mengedit manual di server?

Mental model production:

NGINX config is not a folder collection.
NGINX config is a compiled traffic contract.

The file layout is the source structure.
The rendered nginx -T output is the effective artifact.
The reload is the deployment boundary.

1. Tujuan layout konfigurasi production

Layout yang baik bukan layout yang terlihat rapi. Layout yang baik memenuhi invariant operasional.

InvariantMakna
DeterministicConfig final tidak berubah karena urutan include yang kabur atau file sisa.
ReviewableDiff menunjukkan perubahan traffic contract, bukan noise templating.
Context-safeFile yang dimaksud untuk http tidak bisa sembarangan di-include dari server atau location.
Fail-closedHost tidak dikenal, path tidak dikenal, dan upstream tidak valid tidak jatuh ke aplikasi secara diam-diam.
RollbackableVersi config sebelumnya bisa diaktifkan tanpa mengarang ulang file manual.
ObservableSetiap server/location penting punya log format dan marker yang membantu RCA.
Testablenginx -t, nginx -T, probe HTTP, dan policy check bisa berjalan sebelum reload.
Minimal surpriseEngineer bisa memprediksi efek perubahan tanpa membaca seluruh repository.

Prinsip kerasnya:

A config layout is safe when the blast radius of a file is obvious.

Jika sebuah snippet kecil bisa mengubah header semua domain, menghapus security header tertentu, atau mengubah timeout upstream tanpa terlihat di review, layout itu belum aman.


2. Cara NGINX membaca konfigurasi

NGINX membaca satu main configuration file. Dari file itu, directive include dapat memasukkan file lain. File hasil include bukan “modul runtime dinamis”; ia menjadi bagian dari config yang diparse oleh master process saat start atau reload.

Secara praktis:

nginx.conf
  -> include conf.d/*.conf
      -> include snippets/*.conf

akan diperlakukan seperti satu konfigurasi besar setelah ekspansi include.

Konsekuensinya penting:

  1. File include harus valid di context tempat ia dimasukkan.
  2. Directive yang valid di http belum tentu valid di server.
  3. Directive yang valid di server belum tentu valid di location.
  4. Perubahan file include tidak punya efek sampai NGINX reload.
  5. Kesalahan di satu file include dapat menggagalkan seluruh konfigurasi baru.

Jangan berpikir seperti ini:

snippets/security.conf adalah file mandiri.

Pikirkan seperti ini:

snippets/security.conf adalah potongan syntax yang hanya valid jika ditempel di context tertentu.

Ini alasan kenapa snippet harus diberi nama berdasarkan context.

Buruk:

snippets/security.conf
snippets/proxy.conf
snippets/cache.conf

Lebih aman:

snippets/http/security-maps.conf
snippets/server/security-headers.conf
snippets/location/proxy-common.conf
snippets/location/proxy-streaming.conf

Nama file tidak membuat NGINX lebih aman secara teknis. Tetapi nama file membuat manusia dan CI lebih mudah mencegah include salah tempat.


3. Layout minimal yang cukup untuk production

Berikut layout yang bisa dipakai untuk server VM/bare metal/container non-Kubernetes. Ini bukan satu-satunya layout, tetapi ia memisahkan tanggung jawab dengan jelas.

/etc/nginx/
├── nginx.conf
├── modules-enabled/
│   └── *.conf
├── conf.d/
│   ├── 00-http-core.conf
│   ├── 10-log-formats.conf
│   ├── 20-maps.conf
│   ├── 30-rate-limit-zones.conf
│   └── 40-cache-zones.conf
├── upstreams/
│   ├── app-public-api.upstream.conf
│   ├── app-admin-api.upstream.conf
│   └── static-origin.upstream.conf
├── servers/
│   ├── 000-default-deny.server.conf
│   ├── public-api.example.com.server.conf
│   ├── admin.example.com.server.conf
│   └── static.example.com.server.conf
├── snippets/
│   ├── server/
│   │   ├── tls-modern.conf
│   │   ├── security-headers.conf
│   │   └── error-pages.conf
│   └── location/
│       ├── proxy-common.conf
│       ├── proxy-streaming.conf
│       ├── cors-api.conf
│       └── static-cache.conf
└── generated/
    └── README.md

Pembagian tanggung jawab:

AreaIsiContext target
nginx.confStruktur utama process, events, http includemain, events, http
modules-enabled/Dynamic module loading bila digunakanmain
conf.d/HTTP-wide config seperti log format, map, zoneshttp
upstreams/Upstream poolshttp
servers/Public virtual servershttp berisi server block
snippets/server/Potongan yang hanya valid di serverserver
snippets/location/Potongan yang hanya valid di locationlocation
generated/Output automation, bukan diedit manualtergantung pipeline

Catatan penting: distro tertentu memakai sites-available dan sites-enabled. Itu boleh digunakan, tetapi jangan bergantung pada konvensi distro sebagai desain architecture. Yang penting adalah invariant: ownership jelas, context jelas, include jelas, dan artifact final bisa divalidasi.


4. Main nginx.conf sebagai composition root

nginx.conf sebaiknya menjadi composition root, bukan tempat semua logic.

Contoh baseline:

user nginx;
worker_processes auto;
pid /run/nginx.pid;

# Load dynamic modules here if your distribution uses them.
include /etc/nginx/modules-enabled/*.conf;

events {
    worker_connections 4096;
    multi_accept off;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    server_tokens off;

    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65s;

    # 1. global observability primitives
    include /etc/nginx/conf.d/10-log-formats.conf;

    # 2. decision tables and shared zones
    include /etc/nginx/conf.d/20-maps.conf;
    include /etc/nginx/conf.d/30-rate-limit-zones.conf;
    include /etc/nginx/conf.d/40-cache-zones.conf;

    # 3. upstream contracts
    include /etc/nginx/upstreams/*.upstream.conf;

    # 4. public server contracts
    include /etc/nginx/servers/*.server.conf;
}

Kuncinya bukan directive spesifiknya. Kuncinya adalah urutan konseptual:

global primitives
  -> decision tables
  -> shared memory zones
  -> upstream contracts
  -> server contracts

Kenapa map dan zones diletakkan sebelum server?

Karena server dan location sering memakai variable hasil map, limit_req_zone, proxy_cache_path, atau log format. Secara semantic, dependency harus muncul sebelum consumer. Walaupun beberapa directive tidak “dipakai” sampai request berjalan, layout yang mengikuti dependency membuat review lebih mudah.


5. Default server harus fail-closed

Salah satu anti-pattern paling berbahaya adalah membiarkan request dengan Host tidak dikenal jatuh ke aplikasi utama.

Buruk:

server {
    listen 80;
    server_name app.example.com;

    location / {
        proxy_pass http://app;
    }
}

Jika ini menjadi default server untuk port 80, request dengan Host asing bisa masuk ke aplikasi.

Lebih aman:

server {
    listen 80 default_server;
    listen 443 ssl default_server;
    server_name _;

    ssl_certificate     /etc/nginx/tls/default.crt;
    ssl_certificate_key /etc/nginx/tls/default.key;

    access_log /var/log/nginx/default-deny.access.log main_json;
    return 444;
}

return 444 adalah pola NGINX untuk menutup koneksi tanpa response HTTP. Dalam beberapa environment, kamu mungkin memilih return 404 agar lebih mudah diamati oleh load balancer atau synthetic monitor. Pilihan utamanya bukan angka status; pilihan utamanya adalah jangan forward host tidak dikenal ke upstream bisnis.

Default server harus menjawab pertanyaan:

Apa yang terjadi ketika client mengirim Host yang tidak kita kontrakkan?

Jawaban production yang sehat:

Deny, log, and do not reach application code.

6. Satu server block adalah satu public contract

Jangan membuat server block sebagai “tempat semua routing”. Anggap satu server block sebagai satu public contract:

scheme + port + host + TLS identity + top-level policy

Contoh:

server {
    listen 443 ssl http2;
    server_name public-api.example.com;

    include /etc/nginx/snippets/server/tls-modern.conf;
    include /etc/nginx/snippets/server/security-headers.conf;

    access_log /var/log/nginx/public-api.access.log main_json;
    error_log  /var/log/nginx/public-api.error.log warn;

    location = /healthz {
        access_log off;
        return 204;
    }

    location /v1/ {
        include /etc/nginx/snippets/location/proxy-common.conf;
        proxy_pass http://app_public_api;
    }

    location / {
        return 404;
    }
}

Invariant yang ingin dicapai:

  1. Host terlihat jelas.
  2. TLS policy terlihat jelas.
  3. Access log per public surface terlihat jelas.
  4. Health endpoint tidak menabrak upstream bisnis.
  5. Unknown path tidak diam-diam masuk ke root upstream.

Jangan membuat semua domain masuk satu file besar tanpa boundary:

servers/all-sites.conf

Itu membuat review sulit. Ketika seseorang mengubah location /, reviewer harus membaca semua virtual host untuk memahami efeknya.

Lebih baik satu file per public host atau public contract:

servers/public-api.example.com.server.conf
servers/admin.example.com.server.conf
servers/static.example.com.server.conf

7. Upstream adalah private contract

upstream block bukan sekadar daftar IP. Ia adalah kontrak antara edge dan aplikasi.

Contoh:

upstream app_public_api {
    zone app_public_api 64k;

    server 10.10.1.11:8080 max_fails=3 fail_timeout=10s;
    server 10.10.1.12:8080 max_fails=3 fail_timeout=10s;

    keepalive 64;
}

Hal yang sebaiknya diekspresikan di upstream contract:

KeputusanContoh
Nama upstreamapp_public_api, bukan backend1
Discovery modelstatic IP, DNS, service mesh sidecar, local socket
Failure behaviormax_fails, fail_timeout, retry directive di consumer
Connection reusekeepalive pool
Capacity hintweight bila server tidak setara
Observability namenama upstream muncul di log/metric

Jangan mencampur upstream berbeda di satu nama generik:

upstream backend {
    server app-a:8080;
    server app-b:8080;
}

Nama backend tidak memberi tahu kontrak apa pun. Ia membuat log dan incident response lebih buruk.

Lebih baik:

upstream app_checkout_write_api { ... }
upstream app_checkout_read_api  { ... }
upstream app_checkout_static    { ... }

Nama upstream harus menjawab:

Traffic apa yang aman dikirim ke sini?

8. Snippet harus kecil, context-specific, dan tidak mengejutkan

Snippet adalah pisau tajam. Ia mengurangi duplikasi, tetapi bisa menyembunyikan behavior berbahaya.

Snippet yang baik:

# /etc/nginx/snippets/location/proxy-common.conf
proxy_http_version 1.1;

proxy_set_header Host              $host;
proxy_set_header X-Real-IP         $remote_addr;
proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Request-ID      $request_id;

proxy_connect_timeout 3s;
proxy_send_timeout    30s;
proxy_read_timeout    30s;

Karakteristiknya:

  1. Jelas hanya untuk location proxy.
  2. Tidak mengandung proxy_pass.
  3. Tidak mengandung business routing.
  4. Tidak mengubah status response.
  5. Tidak melakukan include snippet lain.

Snippet yang buruk:

# bad: snippets/proxy.conf
proxy_set_header Host $host;
proxy_read_timeout 300s;
proxy_pass http://app;

Masalahnya:

  1. proxy_pass menyembunyikan tujuan upstream.
  2. Timeout 300s mungkin cocok untuk streaming, tetapi buruk untuk API biasa.
  3. Nama file tidak menunjukkan context.
  4. Reviewer tidak melihat traffic routing di server file.

Rule of thumb:

Snippets may standardize mechanics.
Snippets must not hide business routing.

9. Bedakan snippet umum dan snippet varian

Satu snippet proxy-common.conf biasanya tidak cukup untuk semua traffic. Streaming, upload, gRPC, API biasa, dan static origin punya kebutuhan berbeda.

Contoh varian:

snippets/location/proxy-common.conf
snippets/location/proxy-low-latency-api.conf
snippets/location/proxy-large-upload.conf
snippets/location/proxy-streaming.conf
snippets/location/proxy-grpc-common.conf

proxy-low-latency-api.conf:

include /etc/nginx/snippets/location/proxy-common.conf;

proxy_connect_timeout 1s;
proxy_send_timeout    10s;
proxy_read_timeout    10s;
proxy_buffering on;

proxy-streaming.conf:

include /etc/nginx/snippets/location/proxy-common.conf;

proxy_read_timeout 1h;
proxy_buffering off;
proxy_cache off;

Kamu harus hati-hati dengan nested include. Ia boleh digunakan, tetapi jangan terlalu dalam. Jika snippet A include B, B include C, C include D, config final sulit dibaca.

Batas sehat:

server file -> one or two snippet layers -> no hidden routing

Gunakan nginx -T untuk melihat effective config hasil ekspansi include saat review dan incident.


10. Jangan membuat environment dengan copy-paste penuh

Anti-pattern umum:

environments/
├── dev/nginx.conf
├── staging/nginx.conf
└── prod/nginx.conf

Lalu ketiganya berisi 95% config yang sama dan 5% beda. Dalam beberapa bulan, perbedaannya menjadi tidak disengaja.

Lebih baik bedakan:

base config
  + environment values
  + rendered artifact

Contoh repository:

nginx-config/
├── base/
│   ├── nginx.conf
│   ├── conf.d/
│   ├── upstreams/
│   ├── servers/
│   └── snippets/
├── env/
│   ├── dev.values.yaml
│   ├── staging.values.yaml
│   └── prod.values.yaml
├── rendered/
│   └── .gitkeep
└── tests/
    ├── config-policy.test.sh
    ├── smoke.test.sh
    └── golden/

Pipeline:

values + template
  -> render to temporary directory
  -> nginx -t -c rendered/nginx.conf
  -> nginx -T -c rendered/nginx.conf > artifact/nginx.effective.conf
  -> policy checks against effective config
  -> deploy immutable artifact
  -> reload

Jangan melakukan templating langsung di /etc/nginx pada server production tanpa staging dan validasi. Kesalahan variable kosong bisa menghasilkan behavior valid secara syntax tetapi salah secara traffic.

Contoh bug:

proxy_pass http://${UPSTREAM_NAME};

Jika ${UPSTREAM_NAME} kosong saat render, hasilnya mungkin bukan config yang kamu maksud. Bahkan ketika nginx -t gagal, kamu sudah menulis file live. Lebih aman:

render outside live dir
validate rendered config
switch symlink atomically
reload

11. Separate generated config from hand-written config

Generated config harus terlihat jelas.

Buruk:

/etc/nginx/conf.d/app.conf      # kadang diedit manusia, kadang ditulis automation

Baik:

/etc/nginx/generated/upstreams.generated.conf
/etc/nginx/generated/maps.generated.conf

Tambahkan header:

# GENERATED FILE. DO NOT EDIT.
# Source: service-registry/nginx-upstreams
# Generated at: 2026-07-06T10:15:00+07:00
# Artifact version: nginx-config-20260706.1015.abc123

Kenapa penting?

Saat incident jam 02:00, engineer perlu tahu apakah perubahan manual akan hilang ketika automation berikutnya berjalan.

Rule sederhana:

A file is either owned by humans or owned by automation.
Never both.

12. Include order harus eksplisit

Wildcard include nyaman, tetapi jangan membuat correctness bergantung pada urutan yang tidak terlihat.

Gunakan prefix angka untuk file yang memang memiliki dependency:

conf.d/
├── 00-http-core.conf
├── 10-log-formats.conf
├── 20-maps.conf
├── 30-rate-limit-zones.conf
└── 40-cache-zones.conf

Untuk file yang tidak saling bergantung, nama natural cukup:

servers/
├── admin.example.com.server.conf
├── public-api.example.com.server.conf
└── static.example.com.server.conf

Jangan menaruh map yang dipakai global di dalam file server tertentu:

servers/public-api.example.com.server.conf

lalu berharap server lain bisa memakainya. Itu membuat dependency tersembunyi.

Lebih baik:

conf.d/20-maps.conf

atau jika sangat besar:

maps/
├── 10-routing.map.conf
├── 20-cors.map.conf
└── 30-cache.map.conf

lalu include dari http:

include /etc/nginx/maps/*.map.conf;

13. Pattern file server yang reviewable

Gunakan struktur konsisten di setiap server file.

Template konseptual:

server {
    # 1. listener and identity
    listen 443 ssl http2;
    server_name public-api.example.com;

    # 2. TLS and server-level policy
    include /etc/nginx/snippets/server/tls-modern.conf;
    include /etc/nginx/snippets/server/security-headers.conf;

    # 3. observability
    access_log /var/log/nginx/public-api.access.log main_json;
    error_log  /var/log/nginx/public-api.error.log warn;

    # 4. health and operational endpoints
    location = /healthz {
        access_log off;
        return 204;
    }

    # 5. exact routes before broad routes
    location = /robots.txt {
        root /srv/public-api/static;
    }

    # 6. application routes
    location /v1/ {
        include /etc/nginx/snippets/location/proxy-low-latency-api.conf;
        proxy_pass http://app_public_api;
    }

    # 7. fallback
    location / {
        return 404;
    }
}

Urutan ini bukan diwajibkan NGINX. Ini disiplin manusia.

Manfaatnya:

  1. Reviewer tahu bagian mana yang berubah.
  2. Fallback terlihat jelas.
  3. Route exact tidak tenggelam di tengah file.
  4. Observability tidak lupa.
  5. Business routing tetap terlihat di file server.

14. Jangan membuat location / terlalu kuat

location / adalah fallback prefix. Jika kamu menaruh proxy utama di sana tanpa guard, semua path tidak dikenal masuk aplikasi.

Kadang memang benar untuk monolith atau SPA. Tetapi untuk API gateway, ini sering berbahaya.

Buruk untuk API:

location / {
    proxy_pass http://api;
}

Lebih aman:

location /v1/ {
    proxy_pass http://api_v1;
}

location /v2/ {
    proxy_pass http://api_v2;
}

location / {
    return 404;
}

Jika kamu memakai location / untuk SPA fallback, buat niatnya eksplisit:

location / {
    root /srv/frontend/current;
    try_files $uri $uri/ /index.html;
}

Jangan mencampur API dan SPA fallback tanpa boundary:

location / {
    try_files $uri /index.html;
}

location /api/ {
    proxy_pass http://api;
}

Ini bisa valid, tetapi perlu test eksplisit untuk path seperti /api, /api/, /api/v1, asset missing, dan fallback frontend.


15. Config review checklist

Setiap perubahan NGINX harus bisa dijawab dengan checklist pendek ini.

Identity

Apakah perubahan menambah/mengubah host publik?
Apakah default_server berubah?
Apakah SNI/certificate berubah?
Apakah HTTP->HTTPS redirect berubah?

Routing

Apakah location baru overlap dengan location lama?
Apakah exact/prefix/regex ordering aman?
Apakah fallback jelas?
Apakah proxy_pass slash semantics sudah benar?

Upstream

Apakah upstream name jelas?
Apakah timeout cocok dengan endpoint?
Apakah retry aman untuk method non-idempotent?
Apakah keepalive cocok dengan backend?

Security

Apakah host tidak dikenal fail-closed?
Apakah hidden files/source files terlindungi?
Apakah header security tetap muncul untuk error response bila dibutuhkan?
Apakah real IP trust boundary berubah?

Observability

Apakah access log cukup untuk RCA?
Apakah upstream status/time masuk log?
Apakah request id diteruskan?
Apakah route baru bisa diprobe?

Operations

Apakah nginx -t lulus?
Apakah nginx -T artifact tersimpan?
Apakah rollback path jelas?
Apakah perubahan bisa diaktifkan tanpa restart kasar?

Checklist ini lebih bernilai daripada style guide panjang yang tidak dipakai.


16. Policy check sederhana yang layak dimiliki

Selain nginx -t, buat policy check berbasis grep/regex/AST ringan terhadap effective config.

Contoh shell sederhana:

#!/usr/bin/env bash
set -euo pipefail

CONFIG_DUMP="${1:?usage: check-nginx-policy.sh nginx.effective.conf}"

fail() {
  echo "POLICY FAIL: $*" >&2
  exit 1
}

# No accidental default proxy server.
if grep -n "listen .*default_server" "$CONFIG_DUMP" | grep -qv "000-default-deny"; then
  fail "default_server must be owned by default deny config"
fi

# Avoid broad proxy fallback in API hosts unless explicitly approved.
if grep -n "location / .*{" "$CONFIG_DUMP" >/dev/null && \
   grep -n "proxy_pass http://app_public_api" "$CONFIG_DUMP" >/dev/null; then
  echo "WARN: check whether API host has broad location / proxy fallback" >&2
fi

# Prevent accidental debug logs in production.
if grep -n "error_log .* debug" "$CONFIG_DUMP" >/dev/null; then
  fail "debug error_log is not allowed in production"
fi

Ini bukan pengganti review manusia. Ini guardrail untuk kesalahan yang sering dan mahal.

Jika config repository makin besar, kamu bisa membuat parser sendiri atau memakai test container yang menjalankan synthetic request terhadap NGINX dengan upstream dummy.


17. Golden tests untuk routing

nginx -t hanya memvalidasi syntax dan sebagian resource. Ia tidak membuktikan routing semantics.

Buat test seperti ini:

Request:  GET https://public-api.example.com/v1/orders
Expect:   upstream app_public_api

Request:  GET https://public-api.example.com/admin
Expect:   404, not upstream

Request:  GET https://unknown.example.com/
Expect:   default deny

Request:  GET https://static.example.com/app.js
Expect:   static file or static origin, cache header present

Cara sederhana:

  1. Jalankan NGINX test di container.
  2. Jalankan upstream dummy yang mengembalikan nama service.
  3. Kirim request dengan Host berbeda.
  4. Assert status, header, dan body.

Contoh upstream dummy response:

{
  "upstream": "app_public_api",
  "path": "/v1/orders",
  "method": "GET"
}

Golden tests membuat location matching dan server selection menjadi kontrak yang bisa diuji.


18. Operational artifact: simpan nginx -T

Saat deploy, simpan effective config:

nginx -T -c /etc/nginx/nginx.conf > artifacts/nginx-effective-20260706-101500.conf

Manfaatnya besar:

  1. Incident RCA bisa membandingkan config efektif sebelum/sesudah.
  2. Include expansion terlihat.
  3. Reviewer bisa melihat config final, bukan hanya template.
  4. Rollback bisa memakai artifact yang sudah terbukti valid.

Jangan hanya menyimpan source template. Template tidak selalu sama dengan config runtime, terutama jika ada generated file, environment variable, symlink, atau volume mount.

Production rule:

The effective config is the deploy artifact.
The source config is the authoring artifact.

Keduanya penting, tetapi tidak sama.


19. Anti-pattern taxonomy

Anti-patternKenapa berbahayaPerbaikan
Giant nginx.confBlast radius tidak jelasPecah berdasarkan context dan contract
sites-enabled tanpa reviewSymlink sisa bisa aktifGenerate explicit include set
Snippet berisi proxy_passRouting tersembunyiLetakkan proxy_pass di server file
location / proxy defaultUnknown path masuk appExplicit route + fallback 404
Semua upstream bernama backendLog/RCA burukNama upstream berdasarkan domain fungsi
Copy-paste envDrift tidak disengajaBase + values + rendered artifact
Generated dan manual campurPerubahan hilang/konflikPisahkan ownership file
Tidak menyimpan nginx -TSulit RCASimpan effective config per deploy
Reload tanpa probeValid syntax, behavior salahTambah smoke/golden tests
Default server ke appHost header abuseDefault deny server

20. Reference layout untuk tim platform

Jika kamu membangun platform internal, gunakan repository layout seperti ini:

edge-nginx/
├── README.md
├── Makefile
├── base/
│   ├── nginx.conf
│   ├── conf.d/
│   ├── upstreams/
│   ├── servers/
│   └── snippets/
├── env/
│   ├── dev.yaml
│   ├── staging.yaml
│   └── prod.yaml
├── render/
│   └── render.py
├── tests/
│   ├── policy/
│   ├── golden/
│   └── smoke/
├── docker/
│   ├── Dockerfile.test
│   └── compose.yaml
└── artifacts/
    └── .gitignore

Makefile:

ENV ?= dev
OUT := artifacts/$(ENV)

render:
	python3 render/render.py --env $(ENV) --out $(OUT)

test-config: render
	nginx -t -c $(PWD)/$(OUT)/nginx.conf -p $(PWD)/$(OUT)

dump-config: test-config
	nginx -T -c $(PWD)/$(OUT)/nginx.conf -p $(PWD)/$(OUT) > $(OUT)/nginx.effective.conf

policy: dump-config
	./tests/policy/check-nginx-policy.sh $(OUT)/nginx.effective.conf

smoke: policy
	./tests/smoke/run.sh $(OUT)

Tujuannya bukan membuat pipeline rumit. Tujuannya membuat perubahan edge traffic menjadi sesuatu yang bisa diuji seperti software.


21. Decision matrix: kapan layout perlu lebih kompleks?

KondisiLayout cukup sederhana?Butuh automation?
1 domain staticYaTidak wajib
1 reverse proxy appYaBelum wajib
5–10 domain internalMungkinDisarankan
Banyak upstream dinamisTidakYa
Banyak environmentTidakYa
Regulated/audited systemTidakYa
Multi-team platformTidakYa
Canary/blue-green seringTidakYa

Jangan over-engineer untuk satu website kecil. Tetapi untuk platform yang melayani banyak aplikasi, config layout manual tanpa artifact dan test akan menjadi hutang operasional.


22. Kesimpulan

Layout konfigurasi NGINX production bukan soal preferensi folder. Ia adalah desain kontrol risiko.

Pegang invariant ini:

1. One server block = one public contract.
2. One upstream block = one private contract.
3. Snippets standardize mechanics, not business routing.
4. Default server must fail closed.
5. Generated files and human-owned files must not mix.
6. nginx -t validates syntax; nginx -T shows the effective artifact.
7. Routing behavior needs tests, not just config parsing.
8. Reload is a deployment boundary, not a text-editing habit.

Jika Part 005–010 memberi grammar dan request selection mental model, Part 011 memberi struktur agar grammar itu bisa dipakai oleh tim tanpa menciptakan config jungle.

Part berikutnya akan membahas lifecycle operasionalnya: command line, signal, reload, graceful shutdown, rollback, dan cara memperlakukan NGINX config sebagai deployable artifact.


References

Lesson Recap

You just completed lesson 11 in start here. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.