<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Nicolas Coutin — RSS Feed]]></title><description><![CDATA[Various tips I can provide. Nothing fancy.]]></description><link>https://blog.nicolas-coutin.com</link><generator>GatsbyJS</generator><lastBuildDate>Tue, 12 May 2026 13:53:23 GMT</lastBuildDate><item><title><![CDATA[Dynamic configuration for static Vue 2 apps without SSR]]></title><description><![CDATA[This article explains how to allow our Vue.js (version 2.x) app to accept configurations at run time instead of build time using Docker…]]></description><link>https://blog.nicolas-coutin.com/dynamic-configuration-static-vue-without-ssr/</link><guid isPermaLink="false">https://blog.nicolas-coutin.com/dynamic-configuration-static-vue-without-ssr/</guid><pubDate>Thu, 27 Jun 2019 23:59:00 GMT</pubDate><content:encoded>&lt;p&gt;This article explains how to allow our Vue.js (version 2.x) app to accept configurations at &lt;strong&gt;run time&lt;/strong&gt; instead of &lt;strong&gt;build time&lt;/strong&gt; using &lt;a href=&quot;https://www.docker.com&quot;&gt;Docker&lt;/a&gt; &amp;#x26; &lt;a href=&quot;https://www.nginx.com&quot;&gt;NGINX&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If you want the solution, you can skip to the &lt;em&gt;Solution&lt;/em&gt; section.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Why ?&lt;/h2&gt;
&lt;p&gt;At &lt;a href=&quot;https://www.scalair.fr&quot;&gt;Scalair&lt;/a&gt;, we use Docker to deploy our applications. Some of our applications are Vue.js &lt;strong&gt;static&lt;/strong&gt; front ends, served through NGINX. We use &lt;em&gt;Gitlab CI&lt;/em&gt; to test &amp;#x26; deploy our code to &lt;em&gt;Amazon EKS&lt;/em&gt;. Each Gitlab pipeline builds our app, then pushes the Docker image to Amazon ECR.&lt;/p&gt;
&lt;p&gt;Our Vue apps had configurations like this, &lt;a href=&quot;https://cli.vuejs.org/guide/mode-and-env.html#using-env-variables-in-client-side-code&quot;&gt;thanks to Vue.js client-side env vars&lt;/a&gt; :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;API_URL&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;VUE_APP_API_URL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Due to the lack of &lt;a href=&quot;https://vuejs.org/v2/guide/ssr.html&quot;&gt;Server-Side Rendering&lt;/a&gt; in our Vue app, we used to give environment variables to the Vue CLI at build time in order to provide configurations, e.g.: an API URL.&lt;/p&gt;
&lt;p&gt;We also had a Dockerfile that we used to build using &lt;code class=&quot;language-text&quot;&gt;docker build --build-arg API_URL=https://api.example.com .&lt;/code&gt; :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;dockerfile&quot;&gt;&lt;pre class=&quot;language-dockerfile&quot;&gt;&lt;code class=&quot;language-dockerfile&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Build stage&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; mhart/alpine-node:10 &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; build-stage&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;WORKDIR&lt;/span&gt; /app&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; package.json package-lock.json .npmrc ./&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; npm ci&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; . .&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ARG&lt;/span&gt; API_URL&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;ENV&lt;/span&gt; VUE_APP_API_URL &lt;span class=&quot;token variable&quot;&gt;$API_URL&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; ./node_modules/.bin/vue-cli-service build --mode production&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Production stage&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; nginx:1.17.0-alpine &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; production-stage&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; &lt;span class=&quot;token options&quot;&gt;&lt;span class=&quot;token property&quot;&gt;--from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;build-stage&lt;/span&gt;&lt;/span&gt; /app/dist /usr/share/nginx/html&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;EXPOSE&lt;/span&gt; 80&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;CMD&lt;/span&gt; nginx -g &lt;span class=&quot;token string&quot;&gt;&quot;daemon off;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This multi-stage build will build the static files, then serve them with NGINX.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;One concern we had was the consistency of our images : a Docker image would differ from one environment to an other because of the &lt;code class=&quot;language-text&quot;&gt;--build-arg&lt;/code&gt; parameter. The &lt;code class=&quot;language-text&quot;&gt;VUE_APP_&lt;/code&gt; env vars only work at &lt;strong&gt;build time&lt;/strong&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;p&gt;Let the NGINX container do the job !&lt;/p&gt;
&lt;p&gt;We replaced &lt;code class=&quot;language-text&quot;&gt;process.env.VUE_APP_API_URL&lt;/code&gt; with &lt;code class=&quot;language-text&quot;&gt;&apos;{{ API_URL }}&apos;&lt;/code&gt; :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// This will act as a &quot;template&quot; that will be replaced with an actual URL.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;API_URL&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;{{ API_URL }}&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then we removed the build args &amp;#x26; replaced the &lt;code class=&quot;language-text&quot;&gt;{{ templates }}&lt;/code&gt; before running NGINX.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Final Dockerfile :&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;dockerfile&quot;&gt;&lt;pre class=&quot;language-dockerfile&quot;&gt;&lt;code class=&quot;language-dockerfile&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Build stage&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; mhart/alpine-node:10 &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; build-stage&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;WORKDIR&lt;/span&gt; /app&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; package.json package-lock.json .npmrc ./&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; npm ci&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; . .&lt;/span&gt;

&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;RUN&lt;/span&gt; ./node_modules/.bin/vue-cli-service build --mode production&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Production stage&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; nginx:1.17.0-alpine &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; production-stage&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;COPY&lt;/span&gt; &lt;span class=&quot;token options&quot;&gt;&lt;span class=&quot;token property&quot;&gt;--from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;build-stage&lt;/span&gt;&lt;/span&gt; /app/dist /usr/share/nginx/html&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;EXPOSE&lt;/span&gt; 80&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Using `sed` to replace {{ API_URL }} with the actual API URL,&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# which is given to the container at RUN TIME !&lt;/span&gt;
&lt;span class=&quot;token instruction&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;CMD&lt;/span&gt; sed -i -e &lt;span class=&quot;token string&quot;&gt;&quot;s/{{ API_URL }}/$API_URL/g&quot;&lt;/span&gt; /usr/share/nginx/html/js/app.*.js &amp;amp;&amp;amp; &lt;span class=&quot;token operator&quot;&gt;\&lt;/span&gt;
    nginx -g &lt;span class=&quot;token string&quot;&gt;&quot;daemon off;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Finally, we run the container using these commands :&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; build &lt;span class=&quot;token parameter variable&quot;&gt;-t&lt;/span&gt; my-vue-app &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; run &lt;span class=&quot;token parameter variable&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;token assign-left variable&quot;&gt;API_URL&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;https://api.example.com my-vue-app&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;This is more a temporary solution than an actual long-term solution.&lt;/p&gt;
&lt;p&gt;If you are facing the same issue we had, you should definitely look into &lt;a href=&quot;https://vuejs.org/v2/guide/ssr.html&quot;&gt;Server-Side Rendering&lt;/a&gt;. SSR allows a lot of behaviors that you can&apos;t do with a single static app rendered in the browser. This is a good way to dynamically change parts of your frontend and prevent crappy hacks like we did.&lt;/p&gt;
&lt;h2&gt;Was this article helpful ?&lt;/h2&gt;
&lt;p&gt;Feel free to drop a 👍 !&lt;/p&gt;
&lt;p&gt;This article has been entirely written by a human.&lt;/p&gt;</content:encoded></item></channel></rss>