How to Prepare WAF Environment Variables for a Manual Deploy

●1 ●56
calendar_today ago • schedule2 min read

A manual SafeLine deploy is not finished when .env 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.

What manual preparation actually includes

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 (https://docs.waf.chaitin.com/en/home).

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 /data/safeline with at least 5 GB free, download compose.yaml, write .env, generate secrets, lock permissions, create resource directories, then run docker compose up -d (https://docs.waf.chaitin.com/en/GetStarted/Deploy).

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 uname -m, docker version, docker compose version, cat /proc/cpuinfo | grep "processor", free -h, df -h, and lscpu | grep ssse3.

One misconfiguration: placeholders left in the environment file

The concrete mistake is writing the sample block and treating the bracketed fields as finished values. The deploy guide's .env example includes:

POSTGRES_PASSWORD={postgres-password}
DETECTOR_MGMT_TOKEN=<64-char hex>
MGT_INTERNAL_TOKEN=<64-char hex>
DETECTOR_AES_KEY=<16 alphanumeric characters>

Those strings are fill-in markers. {postgres-password} is not a database password. <64-char hex> is not a token. <16 alphanumeric characters> is not an AES key. A file that still contains those markers has not completed the "Write Environment variables" step, even if SAFELINE_DIR, IMAGE_TAG, MGT_PORT, SUBNET_PREFIX, IMAGE_PREFIX, REGION, MGT_PROXY, DETECTOR_MGMT_AUTH, and DETECTOR_UPDATE_VERIFY were copied correctly.

The same example leaves DETECTOR_UPDATE_PUBLIC_KEY empty. Do not invent a public key, a default Postgres password, or a shared token. The guide does not provide those values.

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 .env, then the two success screens the install guide does document.

Replace only the fields the guide tells you to generate

From the SafeLine directory, generate the three secrets with the documented commands and write them through set_env, which replaces an existing key instead of appending a duplicate.

DETECTOR_MGMT_TOKEN comes from od -An -N32 -tx1 /dev/urandom with spaces and newlines removed. Thirty-two bytes of hex are 64 hexadecimal characters. MGT_INTERNAL_TOKEN uses the same command in a separate run. Do not copy one token into the other variable.

DETECTOR_AES_KEY comes from LC_ALL=C tr -dc 'A-Za-z0-9' </dev/urandom | head -c 16. It must be 16 letters or digits. A shorter cut, a value with punctuation, or the angle-bracket marker is still the original misconfiguration.

Set POSTGRES_PASSWORD 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.

Leave the other sample values in place unless a documented case applies:

  • IMAGE_TAG is the SafeLine version to use. The sample is latest.
  • MGT_PORT is the web console port. The sample is 9443, and the documented console URL uses that port.
  • SUBNET_PREFIX is the internal network address prefix. The sample is 172.22.222.
  • IMAGE_PREFIX is the mirror repository prefix. The sample is chaitin.
  • REGION is shown as -g.
  • MGT_PROXY is the number of console proxy layers. The guide says to use it only when configuring a proxy for the console. The default is 0, which means no proxy is configured. Raising it on a direct console is a different mistake. It does not fix missing secrets.
  • DETECTOR_MGMT_AUTH=on and DETECTOR_UPDATE_VERIFY=on stay as shown.
  • POSTGRES_IMAGE in the x86_64 sample is chaitin/safeline-postgres:15.18.

On ARM, change two lines together: ARCH_SUFFIX=-arm and POSTGRES_IMAGE=chaitin/safeline-postgres-arm:15.18. 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 ARCH_SUFFIX on arm64 is a second pre-flight fault, not a substitute for generated secrets.

For the LTS channel, set RELEASE=-lts. 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 RELEASE empty when you want the sample latest channel.

After set_env writes the three generated values, run chmod 600 .env. The guide repeats that mode in the new-install directory steps, along with chmod 700 on the run, mgt, sock, and postgres data paths, and a symlink from /data/safeline/resources/run/safeline.sock to /var/run/safeline.sock.

Read the file before docker compose up -d. 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. MGT_PROXY should remain 0 unless console proxy layers are actually in use. On ARM, the suffix and the ARM Postgres image must match.

The screen that confirms the fix

Start from /data/safeline with docker compose up -d. The guide says this process may take several minutes.

Open https://<safeline-ip>:9443/ when MGT_PORT is still 9443. The deploy guide says the web console page is what you see. If you changed MGT_PORT, open the port written in .env, not a remembered default. That console page is the first documented confirmation that the manual start reached the management UI.

Then run docker exec safeline-mgt resetadmin. 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 .env, and the guide does not tell you to paste it back into the environment file.

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 (https://docs.waf.chaitin.com/en/GetStarted/AddApplication). A working login only shows that the console account was issued.

If the console page does not appear, return to .env before changing unrelated settings. Confirm the four replaced fields, mode 600, the architecture pair, and MGT_PROXY. The install guide's confirmation path is the console page plus the resetadmin account text. It is not an undocumented log phrase.

Why does the sample contain values I must not keep?

The deploy guide prints a full .env 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.

How long are the two management tokens?

Each documented generator reads 32 bytes with od -An -N32 -tx1 and strips spaces and newlines. That is 64 hexadecimal characters for DETECTOR_MGMT_TOKEN and another 64 for MGT_INTERNAL_TOKEN. Generate them separately.

What is a valid DETECTOR_AES_KEY?

Sixteen characters from A-Z, a-z, and 0-9, produced by the guide's tr and head -c 16 command. Punctuation, spaces, and the angle-bracket placeholder are not valid stand-ins.

Should MGT_PROXY change for a console on port 9443?

No. MGT_PROXY counts console proxy layers and stays 0 when no proxy is configured. The console address is https://<safeline-ip>:9443/ while MGT_PORT remains 9443.

Which screen shows the environment file is fixed?

Two documented results. The browser shows the web console at the host and port from the install, by default https://<safeline-ip>:9443/. Then docker exec safeline-mgt resetadmin prints the initial username admin, an initial password, and Done. If either result is missing, recheck placeholders, token length, AES key length, mode 600, and the ARM image pair before assuming a different fault.


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.

🔥 Join developers growing publicly
Share your knowledge, build in public, and grow your developer presence with a global community.

More Posts

Delivering Database Changes

Steve Fentonverified - Jul 22

Why I Finish the Environment File Before Turning Protection On

Lia - Nov 1

I’m a Senior Dev and I’ve Forgotten How to Think Without a Prompt

Karol Modelski - Mar 19

How to Deploy Your Website for Free (Vercel, Netlify, GitHub Pages)

muhammadfarhan.dev - Aug 14

How to Change the WAF Web Console Listening Port When SafeLine Stays on 9443

Muzilla - Oct 30
chevron_left
1.1k Points • 57 Badges
46Posts
0Comments
Web Dev

Related Jobs

View all jobs →

Commenters (This Week)

1 comment
1 comment

Contribute meaningful comments to climb the leaderboard and earn badges!