<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged configuration</title>
<link>https://coderlegion.com/tag/configuration</link>
<description>Powered by Question2Answer</description>
<item>
<title>How to Prepare WAF Environment Variables for a Manual Deploy</title>
<link>https://coderlegion.com/33371/how-to-prepare-waf-environment-variables-for-a-manual-deploy</link>
<description>&lt;p&gt;A manual SafeLine deploy is not finished when &lt;code&gt;.env&lt;/code&gt; merely contains every key from the sample. The install guide shows a template, then a separate secret-generation section. Stopping after the paste leaves instructions where passwords and tokens must be. That is the misconfiguration this note isolates, together with the screens that confirm it is gone.&lt;/p&gt;
&lt;h3 id=&quot;what-manual-preparation-actually-includes&quot;&gt;What manual preparation actually includes&lt;/h3&gt;
&lt;p&gt;SafeLine is a self-hosted web application firewall. Deployed in front of a web application, it acts as a reverse proxy: clients reach the WAF first, and the WAF filters, monitors, and blocks malicious HTTP/S traffic before forwarding the rest. The product documentation lists defenses for web attacks, proactive bot abused defense, HTML and JS code encryption, IP-based rate limiting, and a web access control list (&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/home&quot;&gt;https://docs.waf.chaitin.com/en/home&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;The deploy guide recommends an automatic installer, then documents a manual path for operators who prepare the host themselves. Manual preparation has a fixed order: install Docker if needed, create &lt;code&gt;/data/safeline&lt;/code&gt; with at least 5 GB free, download &lt;code&gt;compose.yaml&lt;/code&gt;, write &lt;code&gt;.env&lt;/code&gt;, generate secrets, lock permissions, create resource directories, then run &lt;code&gt;docker compose up -d&lt;/code&gt; (&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/GetStarted/Deploy&quot;&gt;https://docs.waf.chaitin.com/en/GetStarted/Deploy&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;Check the host before touching variables. The documented minimums are Linux, x86_64 or arm64, Docker 20.10.14 or above, Docker Compose 2.0.0 or above, 1 CPU core, 1 GB of memory, 5 GB of disk, and a CPU with ssse3. The guide's own checks are &lt;code&gt;uname -m&lt;/code&gt;, &lt;code&gt;docker version&lt;/code&gt;, &lt;code&gt;docker compose version&lt;/code&gt;, &lt;code&gt;cat /proc/cpuinfo | grep &quot;processor&quot;&lt;/code&gt;, &lt;code&gt;free -h&lt;/code&gt;, &lt;code&gt;df -h&lt;/code&gt;, and &lt;code&gt;lscpu | grep ssse3&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;one-misconfiguration-placeholders-left-in-the-environment-file&quot;&gt;One misconfiguration: placeholders left in the environment file&lt;/h3&gt;
&lt;p&gt;The concrete mistake is writing the sample block and treating the bracketed fields as finished values. The deploy guide's &lt;code&gt;.env&lt;/code&gt; example includes:&lt;/p&gt;
&lt;p&gt;POSTGRES_PASSWORD={postgres-password}&lt;br&gt;
DETECTOR_MGMT_TOKEN=&amp;lt;64-char hex&amp;gt;&lt;br&gt;
MGT_INTERNAL_TOKEN=&amp;lt;64-char hex&amp;gt;&lt;br&gt;
DETECTOR_AES_KEY=&amp;lt;16 alphanumeric characters&amp;gt;&lt;/p&gt;
&lt;p&gt;Those strings are fill-in markers. &lt;code&gt;{postgres-password}&lt;/code&gt; is not a database password. &lt;code&gt;&amp;lt;64-char hex&amp;gt;&lt;/code&gt; is not a token. &lt;code&gt;&amp;lt;16 alphanumeric characters&amp;gt;&lt;/code&gt; is not an AES key. A file that still contains those markers has not completed the &quot;Write Environment variables&quot; step, even if &lt;code&gt;SAFELINE_DIR&lt;/code&gt;, &lt;code&gt;IMAGE_TAG&lt;/code&gt;, &lt;code&gt;MGT_PORT&lt;/code&gt;, &lt;code&gt;SUBNET_PREFIX&lt;/code&gt;, &lt;code&gt;IMAGE_PREFIX&lt;/code&gt;, &lt;code&gt;REGION&lt;/code&gt;, &lt;code&gt;MGT_PROXY&lt;/code&gt;, &lt;code&gt;DETECTOR_MGMT_AUTH&lt;/code&gt;, and &lt;code&gt;DETECTOR_UPDATE_VERIFY&lt;/code&gt; were copied correctly.&lt;/p&gt;
&lt;p&gt;The same example leaves &lt;code&gt;DETECTOR_UPDATE_PUBLIC_KEY&lt;/code&gt; empty. Do not invent a public key, a default Postgres password, or a shared token. The guide does not provide those values.&lt;/p&gt;
&lt;p&gt;It also does not publish a dedicated error line for this mistake. Do not promote an unrelated container message into proof. Proof is a reread of &lt;code&gt;.env&lt;/code&gt;, then the two success screens the install guide does document.&lt;/p&gt;
&lt;h3 id=&quot;replace-only-the-fields-the-guide-tells-you-to-generate&quot;&gt;Replace only the fields the guide tells you to generate&lt;/h3&gt;
&lt;p&gt;From the SafeLine directory, generate the three secrets with the documented commands and write them through &lt;code&gt;set_env&lt;/code&gt;, which replaces an existing key instead of appending a duplicate.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;DETECTOR_MGMT_TOKEN&lt;/code&gt; comes from &lt;code&gt;od -An -N32 -tx1 /dev/urandom&lt;/code&gt; with spaces and newlines removed. Thirty-two bytes of hex are 64 hexadecimal characters. &lt;code&gt;MGT_INTERNAL_TOKEN&lt;/code&gt; uses the same command in a separate run. Do not copy one token into the other variable.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;DETECTOR_AES_KEY&lt;/code&gt; comes from &lt;code&gt;LC_ALL=C tr -dc 'A-Za-z0-9' &amp;lt;/dev/urandom | head -c 16&lt;/code&gt;. It must be 16 letters or digits. A shorter cut, a value with punctuation, or the angle-bracket marker is still the original misconfiguration.&lt;/p&gt;
&lt;p&gt;Set &lt;code&gt;POSTGRES_PASSWORD&lt;/code&gt; to a real initialization password you choose and store. The guide defines that variable as the Postgres database initialization password. It does not publish a default.&lt;/p&gt;
&lt;p&gt;Leave the other sample values in place unless a documented case applies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;IMAGE_TAG&lt;/code&gt; is the SafeLine version to use. The sample is &lt;code&gt;latest&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MGT_PORT&lt;/code&gt; is the web console port. The sample is &lt;code&gt;9443&lt;/code&gt;, and the documented console URL uses that port.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SUBNET_PREFIX&lt;/code&gt; is the internal network address prefix. The sample is &lt;code&gt;172.22.222&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IMAGE_PREFIX&lt;/code&gt; is the mirror repository prefix. The sample is &lt;code&gt;chaitin&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;REGION&lt;/code&gt; is shown as &lt;code&gt;-g&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;MGT_PROXY&lt;/code&gt; is the number of console proxy layers. The guide says to use it only when configuring a proxy for the console. The default is &lt;code&gt;0&lt;/code&gt;, which means no proxy is configured. Raising it on a direct console is a different mistake. It does not fix missing secrets.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DETECTOR_MGMT_AUTH=on&lt;/code&gt; and &lt;code&gt;DETECTOR_UPDATE_VERIFY=on&lt;/code&gt; stay as shown.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;POSTGRES_IMAGE&lt;/code&gt; in the x86_64 sample is &lt;code&gt;chaitin/safeline-postgres:15.18&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On ARM, change two lines together: &lt;code&gt;ARCH_SUFFIX=-arm&lt;/code&gt; and &lt;code&gt;POSTGRES_IMAGE=chaitin/safeline-postgres-arm:15.18&lt;/code&gt;. The deploy guide states that SafeLine installed on ARM requires a Pro license to work properly, and that the Personal Edition is not supported on ARM. An empty &lt;code&gt;ARCH_SUFFIX&lt;/code&gt; on arm64 is a second pre-flight fault, not a substitute for generated secrets.&lt;/p&gt;
&lt;p&gt;For the LTS channel, set &lt;code&gt;RELEASE=-lts&lt;/code&gt;. The guide states that the LTS version will no longer be maintained starting from 9.1.0-LTS (14 August 2025), and that LTS users are strongly recommended to upgrade to the latest version. If you have concerns, it recommends upgrading one month after a new release. Leave &lt;code&gt;RELEASE&lt;/code&gt; empty when you want the sample latest channel.&lt;/p&gt;
&lt;p&gt;After &lt;code&gt;set_env&lt;/code&gt; writes the three generated values, run &lt;code&gt;chmod 600 .env&lt;/code&gt;. The guide repeats that mode in the new-install directory steps, along with &lt;code&gt;chmod 700&lt;/code&gt; on the run, mgt, sock, and postgres data paths, and a symlink from &lt;code&gt;/data/safeline/resources/run/safeline.sock&lt;/code&gt; to &lt;code&gt;/var/run/safeline.sock&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Read the file before &lt;code&gt;docker compose up -d&lt;/code&gt;. Both tokens should be 64 hex characters with no spaces. The AES key should be 16 alphanumeric characters. The Postgres password should not contain braces. &lt;code&gt;MGT_PROXY&lt;/code&gt; should remain &lt;code&gt;0&lt;/code&gt; unless console proxy layers are actually in use. On ARM, the suffix and the ARM Postgres image must match.&lt;/p&gt;
&lt;h3 id=&quot;the-screen-that-confirms-the-fix&quot;&gt;The screen that confirms the fix&lt;/h3&gt;
&lt;p&gt;Start from &lt;code&gt;/data/safeline&lt;/code&gt; with &lt;code&gt;docker compose up -d&lt;/code&gt;. The guide says this process may take several minutes.&lt;/p&gt;
&lt;p&gt;Open &lt;code&gt;https://&amp;lt;safeline-ip&amp;gt;:9443/&lt;/code&gt; when &lt;code&gt;MGT_PORT&lt;/code&gt; is still &lt;code&gt;9443&lt;/code&gt;. The deploy guide says the web console page is what you see. If you changed &lt;code&gt;MGT_PORT&lt;/code&gt;, open the port written in &lt;code&gt;.env&lt;/code&gt;, not a remembered default. That console page is the first documented confirmation that the manual start reached the management UI.&lt;/p&gt;
&lt;p&gt;Then run &lt;code&gt;docker exec safeline-mgt resetadmin&lt;/code&gt;. The guide says a successful run tells you to remember the content, then prints the initial username admin, an initial password, and Done. That account text is the second confirmation. Store it. It does not appear in &lt;code&gt;.env&lt;/code&gt;, and the guide does not tell you to paste it back into the environment file.&lt;/p&gt;
&lt;p&gt;Log in with that password. Site protection is a later console step, not an environment-variable step. The application guide says to open Applications, choose Add Application, set Domain, Port, and Upstream, then resolve the domain to the SafeLine server (&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/GetStarted/AddApplication&quot;&gt;https://docs.waf.chaitin.com/en/GetStarted/AddApplication&lt;/a&gt;). A working login only shows that the console account was issued.&lt;/p&gt;
&lt;p&gt;If the console page does not appear, return to &lt;code&gt;.env&lt;/code&gt; before changing unrelated settings. Confirm the four replaced fields, mode &lt;code&gt;600&lt;/code&gt;, the architecture pair, and &lt;code&gt;MGT_PROXY&lt;/code&gt;. The install guide's confirmation path is the console page plus the resetadmin account text. It is not an undocumented log phrase.&lt;/p&gt;
&lt;h4 id=&quot;why-does-the-sample-contain-values-i-must-not-keep-&quot;&gt;Why does the sample contain values I must not keep?&lt;/h4&gt;
&lt;p&gt;The deploy guide prints a full &lt;code&gt;.env&lt;/code&gt; example so the key names exist, then adds secret generation. The brace and angle-bracket strings mark fields to replace. A visual match to the example is not the same as a finished file.&lt;/p&gt;
&lt;h4 id=&quot;how-long-are-the-two-management-tokens-&quot;&gt;How long are the two management tokens?&lt;/h4&gt;
&lt;p&gt;Each documented generator reads 32 bytes with &lt;code&gt;od -An -N32 -tx1&lt;/code&gt; and strips spaces and newlines. That is 64 hexadecimal characters for &lt;code&gt;DETECTOR_MGMT_TOKEN&lt;/code&gt; and another 64 for &lt;code&gt;MGT_INTERNAL_TOKEN&lt;/code&gt;. Generate them separately.&lt;/p&gt;
&lt;h4 id=&quot;what-is-a-valid-detector-aes-key-&quot;&gt;What is a valid DETECTOR_AES_KEY?&lt;/h4&gt;
&lt;p&gt;Sixteen characters from &lt;code&gt;A-Z&lt;/code&gt;, &lt;code&gt;a-z&lt;/code&gt;, and &lt;code&gt;0-9&lt;/code&gt;, produced by the guide's &lt;code&gt;tr&lt;/code&gt; and &lt;code&gt;head -c 16&lt;/code&gt; command. Punctuation, spaces, and the angle-bracket placeholder are not valid stand-ins.&lt;/p&gt;
&lt;h4 id=&quot;should-mgt-proxy-change-for-a-console-on-port-9443-&quot;&gt;Should MGT_PROXY change for a console on port 9443?&lt;/h4&gt;
&lt;p&gt;No. &lt;code&gt;MGT_PROXY&lt;/code&gt; counts console proxy layers and stays &lt;code&gt;0&lt;/code&gt; when no proxy is configured. The console address is &lt;code&gt;https://&amp;lt;safeline-ip&amp;gt;:9443/&lt;/code&gt; while &lt;code&gt;MGT_PORT&lt;/code&gt; remains &lt;code&gt;9443&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&quot;which-screen-shows-the-environment-file-is-fixed-&quot;&gt;Which screen shows the environment file is fixed?&lt;/h4&gt;
&lt;p&gt;Two documented results. The browser shows the web console at the host and port from the install, by default &lt;code&gt;https://&amp;lt;safeline-ip&amp;gt;:9443/&lt;/code&gt;. Then &lt;code&gt;docker exec safeline-mgt resetadmin&lt;/code&gt; prints the initial username admin, an initial password, and Done. If either result is missing, recheck placeholders, token length, AES key length, mode &lt;code&gt;600&lt;/code&gt;, and the ARM image pair before assuming a different fault.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;SafeLine has surpassed 700,000 installations worldwide as of September 30, 2026. SafeLine Pro annual promotional pricing is $650-$880, compared with the regular $1,000 per year.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SafeLine website: &lt;a rel=&quot;nofollow&quot; href=&quot;https://ly.cyberserval.tech/MqhYdmI&quot;&gt;https://ly.cyberserval.tech/MqhYdmI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Demo: &lt;a rel=&quot;nofollow&quot; href=&quot;https://demo.waf.chaitin.com:9443/statistics&quot;&gt;https://demo.waf.chaitin.com:9443/statistics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Official docs: &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/home&quot;&gt;https://docs.waf.chaitin.com/en/home&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/chaitin/SafeLine&quot;&gt;https://github.com/chaitin/SafeLine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Pro offer: &lt;a rel=&quot;nofollow&quot; href=&quot;https://cyberserval.tech/landing/lto&quot;&gt;https://cyberserval.tech/landing/lto&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/33371/how-to-prepare-waf-environment-variables-for-a-manual-deploy</guid>
<pubDate>Mon, 02 Nov 2026 02:55:20 +0000</pubDate>
</item>
<item>
<title>Why I Finish the Environment File Before Turning Protection On</title>
<link>https://coderlegion.com/33370/why-i-finish-the-environment-file-before-turning-protection-on</link>
<description>&lt;p&gt;I do not turn SafeLine protection on the moment the containers start. Protection, in the sense that matters to an application, begins when traffic is sent through the WAF and an application is added in the console. Before that cutover I want a written decision on every environment variable the manual deploy expects. A half-filled .env file is not a minor cleanup task. It is the file that sets the install directory, the image tag, the console port, the database password, the internal network prefix, and the secrets the detector and management services share.&lt;/p&gt;
&lt;p&gt;SafeLine is a self-hosted web application firewall. The introduction describes it as a reverse proxy placed in front of a web application. Clients pass through that proxy before they reach the origin. It filters and monitors HTTP and HTTPS traffic and is documented to block malicious requests such as SQL injection, XSS, code injection, OS command injection, CRLF injection, LDAP injection, XPath injection, RCE, XXE, SSRF, path traversal, backdoor, brute force, HTTP flood, and bot abuse. The same introduction lists core capabilities that include defenses for web attacks, proactive bot-abuse defense, HTML and JavaScript code encryption, IP-based rate limiting, and a web access control list. Feature notes also describe rate limiting, CAPTCHA challenge, authentication challenge, and dynamic protection for HTML and JavaScript. Those controls are only useful after the proxy path is correct, so I treat the environment file as the last gate before I point a domain at the WAF. Source: &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/home&quot;&gt;https://docs.waf.chaitin.com/en/home&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-decision-i-make-before-any-traffic-moves&quot;&gt;The decision I make before any traffic moves&lt;/h3&gt;
&lt;p&gt;The application guide says SafeLine is developed based on nginx. It receives traffic for the original site, cleans malicious attack traffic, and forwards the remaining traffic to the upstream application. Adding an application in the console requires a domain, hostname, or IP, a listen port such as 80 or 443, and the real upstream address. For HTTPS applications the SSL option must be checked. After those fields are saved, the domain has to resolve to the SafeLine server. That DNS change is the moment protection becomes real for users. I will not make it until the manual deploy variables are decided, written, and permission-locked. Source: &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/GetStarted/AddApplication&quot;&gt;https://docs.waf.chaitin.com/en/GetStarted/AddApplication&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Manual deploy is documented separately from the automatic installer. It expects Docker, a directory with at least 5 GB free, a downloaded compose file, and a .env file that you write yourself. Source: &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/GetStarted/Deploy&quot;&gt;https://docs.waf.chaitin.com/en/GetStarted/Deploy&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;checklist-before-i-write-the-file&quot;&gt;Checklist before I write the file&lt;/h3&gt;
&lt;p&gt;I run the host checks from the deploy guide before I choose any value. The documented minimums are Linux, x86_64 or arm64, Docker 20.10.14 or above, Docker Compose 2.0.0 or above, one CPU core, 1 GB of memory, 5 GB of disk, and a CPU with the ssse3 instruction. The same page lists the commands I actually run: uname -m, docker version, docker compose version, a processor listing from /proc/cpuinfo, free -h, df -h, and lscpu piped to a search for ssse3. If any of those checks fail, I stop. I do not plan to turn protection on against an undersized or unsupported host and fix the platform later.&lt;/p&gt;
&lt;p&gt;Then I decide the variables in the official template, not a shortened list of my own.&lt;/p&gt;
&lt;p&gt;Directory and image choices come first. SAFELINE_DIR is the directory SafeLine uses for configuration and data. The guide creates /data/safeline and requires at least 5 GB there. IMAGE_TAG is the SafeLine version to be used. I keep latest only when I have already accepted that channel. IMAGE_PREFIX is the mirror repository prefix. The template sets it to chaitin, and I keep that published value rather than inventing another registry path.&lt;/p&gt;
&lt;p&gt;The release channel is a separate decision. Setting RELEASE=-lts selects the LTS images. The deploy page states that the LTS version will no longer be maintained starting from 9.1.0-LTS on 14 August 2025, and that LTS users are strongly recommended to upgrade to the latest version. If concerns remain, the same note recommends upgrading one month after a new version is released. An empty RELEASE is also a decision: it means I am not selecting the LTS suffix. I write that choice down before I touch the file.&lt;/p&gt;
&lt;p&gt;Architecture is not optional on ARM. For an ARM server the guide says to set ARCH_SUFFIX=-arm and to set POSTGRES_IMAGE to chaitin/safeline-postgres-arm:15.18. On the default template, ARCH_SUFFIX is empty and POSTGRES_IMAGE is chaitin/safeline-postgres:15.18. The same section states that SafeLine installed on ARM requires a Pro license to work properly, and that the Personal Edition is not supported on ARM. If I cannot accept that boundary, I do not continue an ARM manual deploy, and I do not swap in the non-ARM image names to avoid the decision.&lt;/p&gt;
&lt;p&gt;Network and console choices are next. MGT_PORT is the web console port. The template and the later UI step both use 9443, and the console URL is https:// followed by the SafeLine IP, port 9443, and a trailing slash. I change that port only after I have checked that the host can spare it, and I record the chosen port in the access plan before launch. SUBNET_PREFIX is the internal network communication address prefix. The template uses 172.22.222. I compare that prefix with existing Docker and host networks before I keep it. MGT_PROXY is the number of console proxy layers. The guide says it should be used only when configuring a proxy for the console, and that the default of 0 means no proxy is configured. I leave it at 0 unless that console-proxy case is actually mine. The template also includes REGION=-g. The variable glossary on the same page does not define REGION, so I keep the documented example instead of guessing another region code.&lt;/p&gt;
&lt;p&gt;Secrets are generated, not typed from memory. The guide shows three commands run from the SafeLine directory. DETECTOR_MGMT_TOKEN and MGT_INTERNAL_TOKEN each come from 32 random bytes printed as hexadecimal, which matches the template note of a 64-character hex value. DETECTOR_AES_KEY is 16 alphanumeric characters drawn from /dev/urandom. POSTGRES_PASSWORD is the Postgres database initialization password, so I replace the placeholder before the first start. The template also sets DETECTOR_MGMT_AUTH=on and DETECTOR_UPDATE_VERIFY=on, and it leaves DETECTOR_UPDATE_PUBLIC_KEY empty. I copy those lines as published. I do not fill the public-key field with a value the deploy page does not specify. A small set_env helper in the same section updates an existing key or appends it, which is how I avoid leaving a placeholder beside a generated value.&lt;/p&gt;
&lt;h3 id=&quot;how-i-commit-the-file-without-turning-protection-on&quot;&gt;How I commit the file without turning protection on&lt;/h3&gt;
&lt;p&gt;After the decisions are written down, I create /data/safeline, download the compose file into that directory, and write .env. I then set the file mode to 600. The later directory steps also create the runtime path, set the runtime, management, socket, and Postgres data directories to mode 700, set .env to mode 600 again, and link the runtime socket file to /var/run/safeline.sock. Those permission steps are part of the checklist, not optional cleanup after the first request.&lt;/p&gt;
&lt;p&gt;Only then do I run docker compose up -d. The guide says this can take several minutes. A running stack is still not protection. I open the console on the port I chose, run docker exec safeline-mgt resetadmin, and store the initial admin username and password that command prints. I log in and confirm the console is the one I intended. I still do not add an application, and I do not change DNS, until every checklist item above is marked done. Adding the application, resolving the domain to the SafeLine host, and sending traffic are the steps that place the reverse proxy in the user path. That is the turn-on I am trying not to rush.&lt;/p&gt;
&lt;h3 id=&quot;what-i-refuse-to-skip&quot;&gt;What I refuse to skip&lt;/h3&gt;
&lt;p&gt;I keep the checklist short enough to finish on the day of the deploy, and strict enough that a missing secret or an undecided ARM license stops the work. The answers I want in writing are the architecture and image names, the release channel, the directory and disk budget, the console port and proxy-layer count, the subnet prefix, and the generated secrets with their file mode. If one answer is that the team will fix it after go-live, I do not add the application.&lt;/p&gt;
&lt;h4 id=&quot;why-do-i-separate-container-start-from-turning-protection-on-&quot;&gt;Why do I separate container start from turning protection on?&lt;/h4&gt;
&lt;p&gt;Container start only brings up the SafeLine services described in the deploy guide. Protection for a site starts when I add an application and resolve its domain to the SafeLine server, as the application guide describes. I want the environment file finished before that DNS and upstream cutover, because a wrong port, subnet, architecture suffix, or secret is much harder to explain once users are already flowing through the proxy.&lt;/p&gt;
&lt;h4 id=&quot;which-variables-do-i-refuse-to-leave-as-placeholders-&quot;&gt;Which variables do I refuse to leave as placeholders?&lt;/h4&gt;
&lt;p&gt;I replace the Postgres password, the detector management token, the management internal token, and the detector AES key before the first docker compose up -d. The deploy page shows those as generated or chosen values, not as text to leave in the file. I also decide ARCH_SUFFIX, POSTGRES_IMAGE, and RELEASE explicitly, including the decision to leave RELEASE empty when I am not selecting LTS.&lt;/p&gt;
&lt;h4 id=&quot;what-host-facts-can-block-the-whole-checklist-&quot;&gt;What host facts can block the whole checklist?&lt;/h4&gt;
&lt;p&gt;The deploy requirements can block the work before any variable is written. I need Linux on x86_64 or arm64, Docker 20.10.14 or newer, Docker Compose 2.0.0 or newer, at least one core, 1 GB of memory, 5 GB of disk, and ssse3. On ARM I also need to accept that a Pro license is required and that the Personal Edition is not supported. If those facts are not true, I do not continue to the console or to an application entry.&lt;/p&gt;
&lt;h4 id=&quot;should-i-change-mgt-proxy-or-region-while-preparing-the-file-&quot;&gt;Should I change MGT_PROXY or REGION while preparing the file?&lt;/h4&gt;
&lt;p&gt;I set MGT_PROXY to 0 unless I am actually configuring proxy layers for the console, which is the only case the deploy guide describes. I keep REGION=-g because that is the value in the published template, and the variable list does not give me another documented choice. Guessing either value is not part of preparing the file.&lt;/p&gt;
&lt;h4 id=&quot;what-do-i-do-with-the-admin-password-relative-to-this-checklist-&quot;&gt;What do I do with the admin password relative to this checklist?&lt;/h4&gt;
&lt;p&gt;After compose is up, docker exec safeline-mgt resetadmin prints the initial username and password. I treat that output as a secret to store before anyone else uses the console. It is not a substitute for the .env secrets. Both have to be known and restricted before I add an application and call the site protected.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;SafeLine has surpassed 700,000 installations worldwide as of September 30, 2026. SafeLine Pro annual promotional pricing is $650-$880, compared with the regular $1,000 per year.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SafeLine website: &lt;a rel=&quot;nofollow&quot; href=&quot;https://ly.cyberserval.tech/MqhYdmI&quot;&gt;https://ly.cyberserval.tech/MqhYdmI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Demo: &lt;a rel=&quot;nofollow&quot; href=&quot;https://demo.waf.chaitin.com:9443/statistics&quot;&gt;https://demo.waf.chaitin.com:9443/statistics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Official docs: &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.waf.chaitin.com/en/home&quot;&gt;https://docs.waf.chaitin.com/en/home&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/chaitin/SafeLine&quot;&gt;https://github.com/chaitin/SafeLine&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Pro offer: &lt;a rel=&quot;nofollow&quot; href=&quot;https://cyberserval.tech/landing/lto&quot;&gt;https://cyberserval.tech/landing/lto&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/33370/why-i-finish-the-environment-file-before-turning-protection-on</guid>
<pubDate>Mon, 02 Nov 2026 02:54:48 +0000</pubDate>
</item>
<item>
<title>Copilot CLI Model Settings</title>
<link>https://coderlegion.com/33024/copilot-cli-model-settings</link>
<description>&lt;p&gt;When working with &lt;a rel=&quot;nofollow&quot; href=&quot;https://raylabs.app/articles/choosing-github-copilot-cli-models/&quot;&gt;GitHub Copilot&lt;/a&gt;, you may encounter a situation where the selected model changes unexpectedly when moving between different repositories or invocation surfaces. Understanding why this happens requires inspecting your installed CLI version, session state, explicit flags, environment variables, user settings, repository settings, and managed policies before modifying your configuration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Quick solution:&lt;/strong&gt;&lt;br&gt;
To inspect your active model selection and configuration directories, run the following diagnostic command in your terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;github-copilot-cli --help
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For deeper official documentation, consult the &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-programmatic-reference&quot;&gt;Copilot CLI programmatic model configuration&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.github.com/en/copilot/reference/copilot-cli-reference/cli-config-dir-reference&quot;&gt;Copilot CLI configuration directory&lt;/a&gt; guides.&lt;/p&gt;
&lt;h3 id=&quot;introduction-to-copilot-cli-model-settings&quot;&gt;Introduction to Copilot CLI Model Settings&lt;/h3&gt;
&lt;p&gt;The Copilot CLI model settings are typically configured using a JSON file located in your local configuration directory. However, these settings can be dynamically overridden by various runtime layers. To properly manage your environment, you must account for the following precedence factors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Installed CLI version: Minor and major version differences across machines or container environments can alter default fallback models.&lt;/li&gt;
&lt;li&gt;Session state: Active custom agents or resumed chat sessions may retain model bindings from their initial instantiation.&lt;/li&gt;
&lt;li&gt;Explicit flags: Command-line flags passed directly during execution take immediate precedence over stored files.&lt;/li&gt;
&lt;li&gt;Environment variables: Shell or CI pipeline variables can silently override persistent file configurations.&lt;/li&gt;
&lt;li&gt;User and repository settings: Local overrides defined within specific projects can diverge from your global user preferences.&lt;/li&gt;
&lt;li&gt;Managed policies: Enterprise or organizational guardrails can enforce specific models regardless of local developer intent.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;configuring-copilot-cli-model-settings&quot;&gt;Configuring Copilot CLI Model Settings&lt;/h3&gt;
&lt;p&gt;To establish a baseline configuration, create or edit your local settings file to specify your preferred model explicitly. Maintaining a clear JSON structure ensures predictable behavior across different workflows.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;model&quot;: &quot;gpt-4o&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When managing complex environments or multiple &lt;a rel=&quot;nofollow&quot; href=&quot;https://raylabs.app/articles/choosing-between-chatgpt-and-claude-for-developer-workflows/&quot;&gt;developer workflows&lt;/a&gt;, ensuring your project files remain clean is critical. For broader insights into maintaining agent configurations, many developers also reference guides such as &lt;a rel=&quot;nofollow&quot; href=&quot;https://raylabs.app/articles/configuring-mcp-json-files-for-ai-agents/&quot;&gt;Configuring Mcp Json Files For Ai&lt;/a&gt; to align their local setups.&lt;/p&gt;
&lt;h3 id=&quot;understanding-override-precedence&quot;&gt;Understanding Override Precedence&lt;/h3&gt;
&lt;p&gt;When debugging why a model switched without your direct input, evaluate the hierarchy of configuration layers. The execution engine resolves settings in the following strict order from highest to lowest precedence:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Explicit command-line flags&lt;/li&gt;
&lt;li&gt;Active environment variables (such as runtime overrides)&lt;/li&gt;
&lt;li&gt;Repository-level configuration files&lt;/li&gt;
&lt;li&gt;Global user-level settings files&lt;/li&gt;
&lt;li&gt;Enterprise managed policies&lt;/li&gt;
&lt;li&gt;Built-in default model settings&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If a repository contains a local configuration file, it will override your global user settings whenever you execute commands from within that directory. This explains why a model selection appears correct in one project but reverts or changes in another.&lt;/p&gt;
&lt;h3 id=&quot;verifying-copilot-cli-model-settings&quot;&gt;Verifying Copilot CLI Model Settings&lt;/h3&gt;
&lt;p&gt;To confirm which model is currently active without executing token-heavy prompts or logging sensitive data, inspect the local runtime output and help flags. Checking the installed help output reveals active runtime defaults and helps isolate whether an environment variable is actively masking your JSON configuration file.&lt;/p&gt;
&lt;h3 id=&quot;troubleshooting-model-drift-across-repositories&quot;&gt;Troubleshooting Model Drift Across Repositories&lt;/h3&gt;
&lt;p&gt;When model drift occurs between projects, work through this systematic troubleshooting workflow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Inspect local directory contents for hidden &lt;code&gt;.github&lt;/code&gt; or configuration files that might contain repository-specific model overrides.&lt;/li&gt;
&lt;li&gt;Print active environment variables in your current terminal session to check for lingering override strings.&lt;/li&gt;
&lt;li&gt;Compare the behavior of a fresh CLI session against a resumed historical session.&lt;/li&gt;
&lt;li&gt;Validate that your JSON syntax adheres strictly to expected schema requirements without trailing commas or mismatched brackets.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;verification-checklist&quot;&gt;Verification Checklist&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Verify that your JSON configuration file is correctly formatted and placed in the appropriate directory.&lt;/li&gt;
&lt;li&gt;Check your active terminal environment variables for conflicting override parameters.&lt;/li&gt;
&lt;li&gt;Run the installed help command to inspect active overrides in your current environment.&lt;/li&gt;
&lt;li&gt;Test your configuration inside a clean repository directory to ensure consistent model selection.&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/33024/copilot-cli-model-settings</guid>
<pubDate>Thu, 29 Oct 2026 21:03:27 +0000</pubDate>
</item>
<item>
<title>How to Configure and Control Nginx?</title>
<link>https://coderlegion.com/14386/how-to-configure-and-control-nginx</link>
<description>
&lt;p&gt;Hello, I'm Ganesh. I'm building &lt;em&gt;git-lrc&lt;/em&gt;, an AI code reviewer that runs on every commit. It is free, unlimited, and source-available on Github. &lt;a href=&quot;https://github.com/HexmosTech/git-lrc&quot; rel=&quot;noopener noreferrer nofollow&quot;&gt;Star Us&lt;/a&gt; to help devs discover the project. Do give it a try and share your feedback for improving the product.&lt;/p&gt;

&lt;p&gt;In previous article I explinaed how nginx works and what is the architecture of nginx.&lt;/p&gt;

&lt;p&gt;Now let's configure it and controls nginx.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding Nginx Controls
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Reload
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;nginx &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; reload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This signal is used to reload the configuration files of nginx. &lt;br&gt;
It will not stop the nginx processes. &lt;br&gt;
It will just reload the configuration files and start the new worker processes.&lt;br&gt;
If old worker process serving few clients it will be served by old worker process.&lt;/p&gt;

&lt;p&gt;Either master will stop old worker process or it will wait for the old worker process to finish serving the current requests. &lt;/p&gt;

&lt;p&gt;This way if anything has issues nginx will not apply new configuration files.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Quit
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;nginx &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; quit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This signal is used to quit the nginx processes. &lt;/p&gt;

&lt;p&gt;It will stop the nginx processes and wait for the worker processes to finish serving the current requests.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Stop
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;nginx &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; stop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This signal is used to stop the nginx processes.&lt;/p&gt;

&lt;p&gt;It will stop the nginx processes and wait for the worker processes to finish serving the current requests.&lt;/p&gt;

&lt;p&gt;To stop nginx with PID. We can use kill command.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;kill&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-QUIT&lt;/span&gt; &amp;lt;master_pid&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can also find all PID&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;ps &lt;span class=&quot;nt&quot;&gt;-ax&lt;/span&gt; | &lt;span class=&quot;nb&quot;&gt;grep &lt;/span&gt;nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configure Nginx
&lt;/h2&gt;

&lt;p&gt;nginx consists of modules which are controlled by directives specified in the configuration file. &lt;/p&gt;

&lt;p&gt;These Directives are divided into simple directives and block directives.&lt;/p&gt;

&lt;p&gt;Similar to C language, simple directive consists of the name and parameters separated by spaces and ends with a semicolon (;). &lt;/p&gt;

&lt;p&gt;A block directive has the same structure as a simple directive, but instead of the semicolon it ends with a set of additional instructions surrounded by braces ({ and }). &lt;/p&gt;

&lt;p&gt;If a block directive can have other directives inside braces, it is called a context (examples: events, http, server, and location).&lt;/p&gt;

&lt;p&gt;Directives placed in the configuration file outside of any contexts are considered to be in the main context. The events and http directives reside in the main context, server in http, and location in server.&lt;/p&gt;

&lt;p&gt;The rest of a line after the # sign is considered a comment.&lt;/p&gt;

&lt;p&gt;Here is the simple example of nginx configuration file:&lt;/p&gt;

&lt;p&gt;Main context: worker_processes 1; &lt;/p&gt;

&lt;p&gt;events context: worker_connections 1024; &lt;/p&gt;

&lt;p&gt;http context: we define server context here.&lt;/p&gt;

&lt;p&gt;server context: In here we define this to listen port 80 which is http port.&lt;/p&gt;

&lt;p&gt;location context: In here we define the root directory of the website.&lt;/p&gt;

&lt;p&gt;So, any request coming to port 80 will be served by the worker process with the root directory of &lt;code&gt;/var/www/html&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight nginx&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;# This is a comment in the Main Context&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;worker_processes&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 

&lt;span class=&quot;k&quot;&gt;events&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;c1&quot;&gt;# This is a simple directive inside the 'events' context&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;worker_connections&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1024&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;http&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;c1&quot;&gt;# We are now inside the 'http' context&lt;/span&gt;

    &lt;span class=&quot;kn&quot;&gt;server&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
        &lt;span class=&quot;c1&quot;&gt;# The 'server' context is nested inside 'http'&lt;/span&gt;
        &lt;span class=&quot;kn&quot;&gt;listen&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 

        &lt;span class=&quot;kn&quot;&gt;location&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
            &lt;span class=&quot;c1&quot;&gt;# The 'location' context is nested inside 'server'&lt;/span&gt;
            &lt;span class=&quot;kn&quot;&gt;root&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;/var/www/html&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;We understood how to control nginx and how to configure nginx.&lt;/p&gt;

&lt;p&gt;So, this makes solid foundation for us to understand how work with nginx.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/HexmosTech/git-lrc&quot; rel=&quot;noopener noreferrer nofollow&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fabn6nl3iehrohehnxgje.png&quot; alt=&quot;git-lrc&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Any feedback or contributors are welcome! It&amp;rsquo;s online, source-available, and ready for anyone to use. &lt;br&gt;
&amp;#11088; Star it on GitHub: &lt;a href=&quot;https://github.com/HexmosTech/git-lrc&quot; rel=&quot;noopener noreferrer nofollow&quot;&gt;https://github.com/HexmosTech/git-lrc&lt;/a&gt;&lt;/p&gt;


</description>
<category>Other</category>
<guid isPermaLink="true">https://coderlegion.com/14386/how-to-configure-and-control-nginx</guid>
<pubDate>Tue, 21 Apr 2026 02:19:50 +0000</pubDate>
</item>
<item>
<title>Why is Nginx configuration organized as sites-available and sites-enabled?</title>
<link>https://coderlegion.com/14075/why-is-nginx-configuration-organized-as-sites-available-and-sites-enabled</link>
<description>
&lt;p&gt;Hello, I'm Ganesh. I'm building &lt;em&gt;git-lrc&lt;/em&gt;, an AI code reviewer that runs on every commit. It is free, unlimited, and source-available on Github. &lt;a href=&quot;https://github.com/HexmosTech/git-lrc&quot; rel=&quot;noopener noreferrer nofollow&quot;&gt;Star Us&lt;/a&gt; to help devs discover the project. Do give it a try and share your feedback for improving the product.&lt;/p&gt;

&lt;p&gt;In this article, We will exploring how to do nginx configuration directory structure. Why it is organized in this way?&lt;/p&gt;

&lt;h2&gt;
  
  
  How are ngix config files organized?
&lt;/h2&gt;

&lt;p&gt;Majorly nginx config files are organized in the following way:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Main config file: &lt;code&gt;/etc/nginx/nginx.conf&lt;/code&gt;
This deal with setting up global configuration for nginx.&lt;/li&gt;
&lt;li&gt;Sites available directory: &lt;code&gt;/etc/nginx/sites-available&lt;/code&gt;
This deal with setting up sites configuration for nginx. Where we can write multiple site configuration files.&lt;/li&gt;
&lt;li&gt;Sites enabled directory: &lt;code&gt;/etc/nginx/sites-enabled&lt;/code&gt;
This deal with enabling sites configuration for nginx. Where we can enable sites configuration.&lt;/li&gt;
&lt;li&gt;Logs directory: &lt;code&gt;/var/log/nginx&lt;/code&gt;
This deal with logs configuration for nginx. Where we can see logs of nginx.&lt;/li&gt;
&lt;li&gt;Cache directory: &lt;code&gt;/var/cache/nginx&lt;/code&gt;
This deal with cache configuration for nginx. Where we can see cache of nginx.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How to write site configuration file?
&lt;/h2&gt;

&lt;p&gt;A site configuration file is a file that is used to configure a site for nginx.&lt;/p&gt;

&lt;p&gt;Which you can further read in official documentation &lt;a href=&quot;https://nginx.org/en/docs/&quot; rel=&quot;noopener noreferrer nofollow&quot;&gt;https://nginx.org/en/docs/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;These configuration should be written in this directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;/etc/nginx/sites-available
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you write the configuration file. This will not enable the site.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to enable the site?
&lt;/h2&gt;

&lt;p&gt;To enable the site, you need to create a symbolic link from the sites-available directory to the sites-enabled directory.&lt;/p&gt;

&lt;p&gt;For example, if you have a site configuration file named &lt;code&gt;example.com&lt;/code&gt; in the &lt;code&gt;sites-available&lt;/code&gt; directory, you can enable it by running the following command:&lt;/p&gt;

&lt;p&gt;we should always test the configuration file before enabling it.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;nginx &lt;span class=&quot;nt&quot;&gt;-t&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a symbolic link from the sites-available directory to the sites-enabled directory.&lt;/p&gt;

&lt;p&gt;The core reason for using symbolic link is that it will allow us to enable and disable the site without removing the configuration file.&lt;/p&gt;

&lt;p&gt;if we directly put the configuration file in sites-enabled directory, then we need to remove the configuration file to disable the site.&lt;/p&gt;

&lt;p&gt;That means if I want to update small change.&lt;br&gt;
If the configration was copy to sites-enabled directory, then on nginx server restart, the changes will not be reflected.&lt;/p&gt;

&lt;p&gt;so, to make it easier we use symbolic link.&lt;/p&gt;

&lt;p&gt;Now once the site is enabled, we need to restart the nginx server to apply the changes.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;systemctl restart nginx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finaly we can access the site in browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to disable the site?
&lt;/h2&gt;

&lt;p&gt;To disable the site, you need to remove the symbolic link from the sites-enabled directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class=&quot;highlight js-code-highlight&quot;&gt;
&lt;pre class=&quot;highlight shell&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo rm&lt;/span&gt; /etc/nginx/sites-enabled/example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By this process it will not delete the configuration file. We can enable and disable the site without removing the configuration file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;By this we understood how to enable and disable the site in nginx.&lt;/p&gt;

&lt;p&gt;How configuration file is organized in nginx.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/HexmosTech/git-lrc&quot; rel=&quot;noopener noreferrer nofollow&quot;&gt;&lt;img src=&quot;https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fabn6nl3iehrohehnxgje.png&quot; alt=&quot;git-lrc&quot;&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Any feedback or contributors are welcome! It&amp;rsquo;s online, source-available, and ready for anyone to use. &lt;br&gt;
&amp;#11088; Star it on GitHub: &lt;a href=&quot;https://github.com/HexmosTech/git-lrc&quot; rel=&quot;noopener noreferrer nofollow&quot;&gt;https://github.com/HexmosTech/git-lrc&lt;/a&gt;&lt;/p&gt;


</description>
<category>Other</category>
<guid isPermaLink="true">https://coderlegion.com/14075/why-is-nginx-configuration-organized-as-sites-available-and-sites-enabled</guid>
<pubDate>Thu, 16 Apr 2026 23:34:22 +0000</pubDate>
</item>
<item>
<title>Loosely coupled configuration for Home Assistant</title>
<link>https://coderlegion.com/4100/loosely-coupled-configuration-for-home-assistant</link>
<description>&lt;p&gt;This post will be short, but I hope it prove to be useful.&lt;/p&gt;
&lt;p&gt;My home is getting more and more connected, and the number of my automations grows each passing month. Recently, I equipped my roller shutters with connected Somfy engines so they could roll down automatically when it's too hot in summer. Spoiler: given the current heatwave, it's a boon!&lt;/p&gt;
&lt;p&gt;I naively created the following automation configuration:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;- id: '1742926520608'
  alias: Close all shutters
  description: Close all shutters if it's already hot in the morning
  triggers:
  - trigger: time                                                      #1
    at: 07:00:00
  conditions:
  - condition: numeric_state
    entity_id: sensor.saint_julien_en_genevois_temperature
    above: 23                                                          #2
  actions:
  - action: cover.close_cover                                          #3
    metadata: {}
    data: {}
    target:
      entity_id: cover.all_shutters                                    #4
  mode: single
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;If at 7 AM local time&lt;/li&gt;
&lt;li&gt;The temperature where I live is higher than 23°C&lt;/li&gt;
&lt;li&gt;Close&lt;/li&gt;
&lt;li&gt;All shutters&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Note the entity ID &lt;code&gt;cover.all_shutters&lt;/code&gt;: it's not an out-of-the-box entity. I explicitly created a group for it:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;cover:
  - platform: group
    name: &quot;All shutters&quot;
    entities:
      - cover.left_shutter
      - cover.middle_shutter
      - cover.right_shutter
      - cover.bedroom_shutter
      - cover.office_shutter
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I copied the same approach to switch all the lights off when I leave home. I soon realized there were flaws in this approach.&lt;/p&gt;
&lt;p&gt;While the probability of adding more shutters is pretty low, I'll likely add more smart lights in the future. Every time I add one, I'll need to put it in the group. Also, I added lights in waves; I ID'd the lights in French at first, but later switched to English. As I renamed them all in English to be coherent, I had to update my dashboard to adapt to the new IDs.&lt;/p&gt;
&lt;p&gt;Home Assistant already has everything to cater for the above, &lt;em&gt;Areas&lt;/em&gt;. You can–and you probably should-assign every device to an Area. Once done, you can replace the &lt;code&gt;target&lt;/code&gt; part of the automation with a list of &lt;code&gt;area_id&lt;/code&gt; instead of &lt;code&gt;entity_id&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;- id: '1742926520608'
  alias: Close all shutters
  description: Close all shutters if it's already hot in the morning
  triggers:
  - trigger: time
    at: 07:00:00
  conditions:
  - condition: numeric_state
    entity_id: sensor.saint_julien_en_genevois_temperature
    above: 23
  actions:
  - action: cover.close_cover
    metadata: {}
    data: {}
    target:
      area_id:                                                         #1
        - office
        - bedroom
        - living_room
  mode: single
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;Now, Home Assistant closes all shutters that are in any of these areas&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Areas tend to be much more stable than individual devices. How often do you add a new area to your home? Granted, an area doesn't explicitly map to a room, but I think that's a better approach than my original one. Home Assistant will now manage every new device of the same type added to the area, &lt;em&gt;e.g.&lt;/em&gt;, if you motorize a new shutter or add a new light.&lt;/p&gt;
&lt;p&gt;There's one drawback to this approach. UI Tiles accept only a single entity ID. Thus, if you want to use the GUI, you must create a group as I did in my first approach.&lt;/p&gt;
&lt;p&gt;In this post, I showed how you could replace the explicit grouping of devices by areas. It's the same idea of loose coupling we have in software, but applied to Home Assistant configuration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;To go further:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.home-assistant.io/integrations/group/&quot;&gt;Group&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.home-assistant.io/docs/scripts/perform-actions/#targeting-areas-and-devices&quot;&gt;Targeting areas and devices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published at &lt;a rel=&quot;nofollow&quot; href=&quot;https://blog.frankel.ch/home-assistant/8/&quot;&gt;A Java Geek&lt;/a&gt; on July 13&lt;sup&gt;th&lt;/sup&gt;, 2025&lt;/em&gt;&lt;/p&gt;
</description>
<category>Backend Dev</category>
<guid isPermaLink="true">https://coderlegion.com/4100/loosely-coupled-configuration-for-home-assistant</guid>
<pubDate>Thu, 17 Jul 2025 19:22:03 +0000</pubDate>
</item>
</channel>
</rss>