diff --git a/templates/gitlab-ci-docker-compose.yml b/templates/gitlab-ci-docker-compose.yml
index 431a3ad8e569b9c137700849ca3c9ab2387b454e..95e46550e145f582f4941e270c8fa7154314f08c 100644
--- a/templates/gitlab-ci-docker-compose.yml
+++ b/templates/gitlab-ci-docker-compose.yml
@@ -440,9 +440,78 @@ stages:
     echo "$1" | tr '[:lower:]' '[:upper:]' | tr '[:punct:]' '_'
   }
 
-  function awkenvsubst() {
-    # escapes '&' char in variables for gsub
-    awk '{while(match($0,"[$%]{[^}]*}")) {var=substr($0,RSTART+2,RLENGTH-3);val=ENVIRON[var];gsub("&","\\\\&",val);gsub("[$%]{"var"}",val)}}1'
+  function tbc_envsubst() {
+    awk '
+      BEGIN {
+        count_replaced_lines = 0
+        # ASCII codes
+        for (i=0; i<=255; i++)
+          char2code[sprintf("%c", i)] = i
+      }
+      # determine encoding (from env or from file extension)
+      function encoding() {
+        enc = ENVIRON["TBC_ENVSUBST_ENCODING"]
+        if (enc != "")
+          return enc
+        if (match(FILENAME, /\.(json|yaml|yml)$/))
+          return "jsonstr"
+        return "raw"
+      }
+      # see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
+      function uriencode(str) {
+        len = length(str)
+        enc = ""
+        for (i=1; i<=len; i++) {
+          c = substr(str, i, 1);
+          if (index("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'\''()", c))
+            enc = enc c
+          else
+            enc = enc "%" sprintf("%02X", char2code[c])
+        }
+        return enc
+      }
+      !/# *nosubst/ {
+        orig_line = $0
+        line = $0
+        count_repl_in_line = 0
+        # /!\ 3rd arg (match) not supported in BusyBox awk
+        while (match(line, /[$%]\{([[:alnum:]_]+)\}/)) {
+          expr_start = RSTART
+          expr_len = RLENGTH
+          # get var name
+          var = substr(line, expr_start+2, expr_len-3)
+          # get var value (from env)
+          val = ENVIRON[var]
+          # check variable is set
+          if (val == "") {
+            printf("[\033[1;93mWARN\033[0m] Environment variable \033[33;1m%s\033[0m is not set or empty\n", var) > "/dev/stderr"
+          } else {
+            enc = encoding()
+            if (enc == "jsonstr") {
+              gsub(/["\\]/, "\\\\&", val)
+              gsub("\n", "\\n", val)
+              gsub("\r", "\\r", val)
+              gsub("\t", "\\t", val)
+            } else if (enc == "uricomp") {
+              val = uriencode(val)
+            } else if (enc == "raw") {
+            } else {
+              printf("[\033[1;93mWARN\033[0m] Unsupported encoding \033[33;1m%s\033[0m: ignored\n", enc) > "/dev/stderr"
+            }
+          }
+          # replace expression in line
+          line = substr(line, 1, expr_start - 1) val substr(line, expr_start + expr_len)
+          count_repl_in_line++
+        }
+        if (count_repl_in_line) {
+          if (count_replaced_lines == 0)
+            printf("[\033[1;94mINFO\033[0m] Variable expansion occurred in file \033[33;1m%s\033[0m:\n", FILENAME) > "/dev/stderr"
+          count_replaced_lines++
+          printf("> line %s: %s\n", NR, orig_line) > "/dev/stderr"
+        }
+        print line
+      }
+    ' "$@"
   }
 
   function configure_network() {
@@ -450,7 +519,7 @@ stages:
     if [[ -f ".netrc" ]]
     then
       log_info "--- \\e[32m.netrc\\e[0m file found: envsubst and install"
-      awkenvsubst < .netrc > ~/.netrc
+      tbc_envsubst .netrc > ~/.netrc
       chmod 0600 ~/.netrc
     fi
 
@@ -509,7 +578,7 @@ stages:
       # special variable supported
       TBC_CI_REGISTRY_TOKEN=$(echo -n "$CI_REGISTRY_USER:$CI_REGISTRY_PASSWORD" | base64 | tr -d '\n')
       export TBC_CI_REGISTRY_TOKEN 
-      awkenvsubst < .docker/config.json > ~/.docker/config.json
+      tbc_envsubst .docker/config.json > ~/.docker/config.json
     else
       log_info "--- \\e[32m.docker/config.json\\e[0m file not found: looking for TBC built images..."
       _image_vars=$(env | awk -F '=' "/^[A-Z]+_(SNAPSHOT|RELEASE)_IMAGE=/ {print \$1}" | sort | uniq)
@@ -664,7 +733,7 @@ stages:
   function compose_up() {
     environment_url=${ENV_URL:-$DCMP_ENVIRONMENT_URL}
     # variables expansion in $environment_url
-    environment_url=$(echo "$environment_url" | awkenvsubst)
+    environment_url=$(echo "$environment_url" | TBC_ENVSUBST_ENCODING=uricomp tbc_envsubst)
     export environment_url
     # extract hostname from $environment_url
     hostname=$(echo "$environment_url" | awk -F[/:] '{print $4}')