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.
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.
| Invariant | Makna |
|---|---|
| Deterministic | Config final tidak berubah karena urutan include yang kabur atau file sisa. |
| Reviewable | Diff menunjukkan perubahan traffic contract, bukan noise templating. |
| Context-safe | File yang dimaksud untuk http tidak bisa sembarangan di-include dari server atau location. |
| Fail-closed | Host tidak dikenal, path tidak dikenal, dan upstream tidak valid tidak jatuh ke aplikasi secara diam-diam. |
| Rollbackable | Versi config sebelumnya bisa diaktifkan tanpa mengarang ulang file manual. |
| Observable | Setiap server/location penting punya log format dan marker yang membantu RCA. |
| Testable | nginx -t, nginx -T, probe HTTP, dan policy check bisa berjalan sebelum reload. |
| Minimal surprise | Engineer 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:
- File include harus valid di context tempat ia dimasukkan.
- Directive yang valid di
httpbelum tentu valid diserver. - Directive yang valid di
serverbelum tentu valid dilocation. - Perubahan file include tidak punya efek sampai NGINX reload.
- 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:
| Area | Isi | Context target |
|---|---|---|
nginx.conf | Struktur utama process, events, http include | main, events, http |
modules-enabled/ | Dynamic module loading bila digunakan | main |
conf.d/ | HTTP-wide config seperti log format, map, zones | http |
upstreams/ | Upstream pools | http |
servers/ | Public virtual servers | http berisi server block |
snippets/server/ | Potongan yang hanya valid di server | server |
snippets/location/ | Potongan yang hanya valid di location | location |
generated/ | Output automation, bukan diedit manual | tergantung 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:
- Host terlihat jelas.
- TLS policy terlihat jelas.
- Access log per public surface terlihat jelas.
- Health endpoint tidak menabrak upstream bisnis.
- 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:
| Keputusan | Contoh |
|---|---|
| Nama upstream | app_public_api, bukan backend1 |
| Discovery model | static IP, DNS, service mesh sidecar, local socket |
| Failure behavior | max_fails, fail_timeout, retry directive di consumer |
| Connection reuse | keepalive pool |
| Capacity hint | weight bila server tidak setara |
| Observability name | nama 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:
- Jelas hanya untuk
locationproxy. - Tidak mengandung
proxy_pass. - Tidak mengandung business routing.
- Tidak mengubah status response.
- 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:
proxy_passmenyembunyikan tujuan upstream.- Timeout 300s mungkin cocok untuk streaming, tetapi buruk untuk API biasa.
- Nama file tidak menunjukkan context.
- Reviewer tidak melihat traffic routing di
serverfile.
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:
- Reviewer tahu bagian mana yang berubah.
- Fallback terlihat jelas.
- Route exact tidak tenggelam di tengah file.
- Observability tidak lupa.
- 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:
- Jalankan NGINX test di container.
- Jalankan upstream dummy yang mengembalikan nama service.
- Kirim request dengan
Hostberbeda. - 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:
- Incident RCA bisa membandingkan config efektif sebelum/sesudah.
- Include expansion terlihat.
- Reviewer bisa melihat config final, bukan hanya template.
- 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-pattern | Kenapa berbahaya | Perbaikan |
|---|---|---|
Giant nginx.conf | Blast radius tidak jelas | Pecah berdasarkan context dan contract |
sites-enabled tanpa review | Symlink sisa bisa aktif | Generate explicit include set |
Snippet berisi proxy_pass | Routing tersembunyi | Letakkan proxy_pass di server file |
location / proxy default | Unknown path masuk app | Explicit route + fallback 404 |
Semua upstream bernama backend | Log/RCA buruk | Nama upstream berdasarkan domain fungsi |
| Copy-paste env | Drift tidak disengaja | Base + values + rendered artifact |
| Generated dan manual campur | Perubahan hilang/konflik | Pisahkan ownership file |
Tidak menyimpan nginx -T | Sulit RCA | Simpan effective config per deploy |
| Reload tanpa probe | Valid syntax, behavior salah | Tambah smoke/golden tests |
| Default server ke app | Host header abuse | Default 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?
| Kondisi | Layout cukup sederhana? | Butuh automation? |
|---|---|---|
| 1 domain static | Ya | Tidak wajib |
| 1 reverse proxy app | Ya | Belum wajib |
| 5–10 domain internal | Mungkin | Disarankan |
| Banyak upstream dinamis | Tidak | Ya |
| Banyak environment | Tidak | Ya |
| Regulated/audited system | Tidak | Ya |
| Multi-team platform | Tidak | Ya |
| Canary/blue-green sering | Tidak | Ya |
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
- NGINX Documentation — Beginner's Guide: https://nginx.org/en/docs/beginners_guide.html
- NGINX Documentation — Command-line Parameters: https://nginx.org/en/docs/switches.html
- NGINX Documentation — Controlling nginx: https://nginx.org/en/docs/control.html
- NGINX Documentation — Core Module: https://nginx.org/en/docs/ngx_core_module.html
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.
Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.