{"id":45089,"date":"2026-03-31T15:02:22","date_gmt":"2026-03-31T12:02:22","guid":{"rendered":"http:\/\/datalabsua.com\/ua\/?p=45089"},"modified":"2026-03-31T15:22:41","modified_gmt":"2026-03-31T12:22:41","slug":"qlik-deployment-best-practices","status":"publish","type":"post","link":"https:\/\/datalabsua.com\/en\/qlik-deployment-best-practices\/","title":{"rendered":"Qlik Deployment Best Practices: From Manual Chaos to Reliable Releases"},"content":{"rendered":"<p>Are you the type of person who deploys Qlik apps by simply exporting a QVF, renaming it, and then importing it to your target environment? If so you&#8217;re not alone! In fact, in almost every Qlik team we&#8217;ve worked with this is actually a common practice. And in almost every single case, they&#8217;ve had to deal with the fallout when something goes wrong and they have to deploy an app in a rush to fix a problem. So for anyone that has &#8220;not had to manually remove a broken deployment after trying to deploy a QVF file, while trying to meet a business critical deadline&#8221; \u2014 congratulations. You&#8217;re in the minority.<\/p>\n<p>The fact that we have to manually deploy stuff to production doesn&#8217;t necessarily mean that things are broken. No, manual deployments work. They work until they stop working. They stop working when the wrong version is deployed. They stop working when a Friday afternoon deployment breaks a critical dashboard that 200 users rely on for work on Monday. They stop working when the need to roll back to the previous deployment hits when there is nothing to roll back to.<\/p>\n<p>This guide provides information on the processes involved in deploying stable Qlik environments, while having an unstable development environment. This guide applies whether you are a solo developer with five applications or a development team of ten, managing 100 or more applications.<\/p>\n<h3>1. Separate Your Environments \u2014 And Mean It<\/h3>\n<p>Every Qlik deployment should flow through at least three environments:<\/p>\n<ul>\n<li><strong>Development (DEV)<\/strong> \u2014 where developers build and experiment freely<\/li>\n<li><strong>Testing \/ QA (TEST)<\/strong>, the stage where the upcoming changes are validated before deployment.<\/li>\n<li><strong>Production (PROD)<\/strong> \u2014 where end users consume dashboards and reports<\/li>\n<\/ul>\n<p>One of the more common misunderstandings in the DevOps world is treating DEV and PROD as the same server. Publishing to the stream that the end users see is not a deployment process, it&#8217;s simply publishing.<\/p>\n<p><strong>Best practice:<\/strong> We recommend that you use separate sites in Qlik Sense (or separate tenants in Qlik Cloud) for each environment. If budget constraints make it impossible to have separate servers, then we recommend at least having separate streams and strict publish rights.<\/p>\n<p><strong>The golden rule:<\/strong> There should be no developer with publish access to the Production stream. Period.<\/p>\n<h3>2. Never Deploy from a Developer&#8217;s Desktop<\/h3>\n<p>One of the most common Qlik deployment anti-patterns:<\/p>\n<ol>\n<li>Developer opens Qlik Sense Desktop<\/li>\n<li>Builds or modifies an app<\/li>\n<li>Exports QVF from Desktop<\/li>\n<li>Imports QVF into the server environment<\/li>\n<li>Publishes to a stream<\/li>\n<\/ol>\n<p>Every step in this chain is a risk:<\/p>\n<ul>\n<li>The Desktop version might differ from the server version<\/li>\n<li>Data connections on Desktop don&#8217;t match server connections<\/li>\n<li>Section Access might reference local groups instead of server groups<\/li>\n<li>Nobody knows which Desktop export is the &#8220;real&#8221; current version<\/li>\n<\/ul>\n<p><strong>Best practice:<\/strong> Always develop in the DEV environment from the server, and never from the Desktop. If the former is not possible in specific situations then the latter should be used but only up to the point that the application is re-deployed to the DEV server for further testing before it is promoted to the TEST environment.<\/p>\n<h3>3. Create a Deployment Checklist<\/h3>\n<p>Before any app moves from one environment to another, verify:<\/p>\n<ul>\n<li>Data connections are referring to the correct environment. DEV databases vs PROD databases<\/li>\n<li>Section Access rules are correct for the target environment<\/li>\n<li>All variables reference the right paths and servers<\/li>\n<li>Load script doesn&#8217;t contain hardcoded paths from another environment<\/li>\n<li>The application deployed successfully in the source environment<\/li>\n<li>Test results have been provided for the proposed changes<\/li>\n<li>A non-developer member has reviewed the changes (if applicable)<\/li>\n<\/ul>\n<p>A written checklist helps catch the typos and errors that often fall through the cracks of memory. It&#8217;s an easy one to make better: print it out, pin it to a spot above your keyboard so it&#8217;s always in view, and use it every single time.<\/p>\n<h3>4. Version Your Apps \u2014 Even If You Do It Manually<\/h3>\n<p>The minimum viable version control for Qlik apps:<\/p>\n<ol>\n<li>Before every deployment, export the current production QVF<\/li>\n<li>Name it with a date and version: <code>Sales-Dashboard_v2.3_2026-03-15.qvf<\/code><\/li>\n<li>Save the file to a Team Shared Folder that everyone can see<\/li>\n<li>Keep at least the last 5 versions<\/li>\n<\/ol>\n<p>This is not great. But this is infinitely better than having no option to roll back at all.<\/p>\n<p>QVF files are binary format, so you can&#8217;t diff them. You can&#8217;t look at the changelog between versions, say, v2.2 and v2.3 and wonder what changed. If you need to roll back to an older version, you can only hope the blob that made it into the next version isn&#8217;t drastically different.<\/p>\n<p>All the above methods have some drawbacks. The best method is to split your application into human readable JSON\/YAML sources and commit those to Git. This gives you proper version control of your application. All changes to formulas and variables will appear as single line changes in Git Diff, and you can do proper branching and merging of code (as you would with any other project code in your company). This is where <a href=\"https:\/\/qops.datalabsua.com\">QOps<\/a> can help, in transforming the Qlik binary format into a more Git friendly, human readable format.<\/p>\n<h3>5. Standardize Your Promotion Process<\/h3>\n<p>Have a clear definition of how applications move from one environment to another, and make this process approachable to be easily deployed through automated tools, and to be straightforward enough for any member of the engineering team to deploy by hand in an emergency, without relying on &#8220;tribal knowledge&#8221;.<\/p>\n<p>A solid promotion process looks like:<\/p>\n<pre><code>DEV \u2192 (export) \u2192 TEST \u2192 (validate) \u2192 PROD\n\u2193                    \u2193\nChange log          Sign-off\nupdated             required\n<\/code><\/pre>\n<p><strong>For each promotion:<\/strong><\/p>\n<ul>\n<li>The developer initiates the request (not self-approves)<\/li>\n<li>The change is documented: what changed, why, and what to test<\/li>\n<li>Someone validates in TEST before production publish<\/li>\n<li>The application for PROD should be done by the team member assigned to this task and not by the developer themselves<\/li>\n<\/ul>\n<p>Who benefits from separation of duties? Anyone whose site experiences unintended changes following a &#8220;deploy of the day&#8221;. The reason the application was created, is being lost in a single person being both author and deployment specialist. Every time a change is deployed, there are always typos, missing variables or misplaced tags. Even having a second pair of eyes catch these little errors has proven to be a remarkable improvement to reliability.<\/p>\n<h3>6. Handle Data Connections Per Environment<\/h3>\n<p>One of the trickiest parts of Qlik deployment: data connections.<\/p>\n<p>Your application points to the development database in DEV, and to the production database in PROD. When you export a QVF from DEV and import it into PROD, the application references will include the data connections for the wrong database.<\/p>\n<p><strong>Options for handling this:<\/strong><\/p>\n<ol>\n<li><strong>Convention for named connection<\/strong> \u2014 We would name the connection in each environment the same, but configure each server to point to a different database. So in our app we&#8217;d refer to <code>Sales_DB<\/code> in every environment. However, on DEV it would be pointing to dev-sql01 and on PROD pointing to prod-sql01.<\/li>\n<li><strong>Variable-based connections<\/strong> \u2014 Store the connection string in a Qlik variable and set the variable for each environment in the properties file or include script.<\/li>\n<li><strong>Automated substitution<\/strong> \u2014 Use deployment tooling to automatically swap out the connection references for the production environment during deployments.<\/li>\n<\/ol>\n<p>We probably don&#8217;t need to say this, but manually editing connections after each import is error-prone and won&#8217;t scale.<\/p>\n<h3>7. Don&#8217;t Skip Testing<\/h3>\n<p>&#8220;It works in DEV&#8221; is not a deployment sign-off.<\/p>\n<p>Before promoting to production, verify in the TEST environment:<\/p>\n<ul>\n<li><strong>Data loads complete successfully<\/strong> \u2014 same data volumes, same schedules<\/li>\n<li><strong>All visualizations render correctly<\/strong> \u2014 check for broken expressions, missing dimensions<\/li>\n<li><strong>Section Access works<\/strong> \u2014 Login as different roles to test access<\/li>\n<li><strong>Performance is acceptable<\/strong> \u2014 TEST should have similar data volumes to PROD. This means that data should be both recently and historically synchronised between environments to maintain accuracy and enable effective testing.<\/li>\n<li><strong>No regression<\/strong> \u2014 features that worked before still work after the change<\/li>\n<\/ul>\n<p>If you can&#8217;t test, you can&#8217;t deploy. An untested deployment into production may be expensive enough to buy all the time in the world.<\/p>\n<h3>8. Keep a Change Log<\/h3>\n<p>For every deployment to production, record:<\/p>\n<ul>\n<li><strong>Date and time<\/strong> of deployment<\/li>\n<li><strong>Who deployed<\/strong> the change<\/li>\n<li><strong>What changed<\/strong> \u2014 specific apps, specific modifications<\/li>\n<li><strong>Why<\/strong> \u2014 the business reason or ticket number<\/li>\n<li><strong>How to roll back<\/strong> if something goes wrong<\/li>\n<\/ul>\n<p>This change log becomes invaluable when:<\/p>\n<ul>\n<li>An executive asks &#8220;what changed last week?&#8221;<\/li>\n<li>Something breaks and you need to identify which deployment caused it<\/li>\n<li>An auditor asks for evidence of change management controls<\/li>\n<\/ul>\n<p>It doesn&#8217;t really matter how you document this information; a shared spreadsheet, wiki page or even a Git commit history of the work that has been done will suffice.<\/p>\n<h3>9. Automate What You Can<\/h3>\n<p>Manual deployments don&#8217;t scale. Where this holds true is with small organisations with 5 applications and a handful of administrators who are willing and able to follow a small checklist. However with 50+ applications in a deployable software estate and a similar number of people trying to deploy to it on a daily basis, errors will creep in and manual procedures will fall apart.<\/p>\n<p>What can be automated in Qlik deployments:<\/p>\n<ul>\n<li><strong>App export and import<\/strong> \u2014 via Qlik Sense Repository API or PowerShell<\/li>\n<li><strong>Data connection configuration<\/strong> \u2014 environment-specific config files<\/li>\n<li><strong>Reload scheduling<\/strong> \u2014 task creation via API after deployment<\/li>\n<li><strong>Notification<\/strong> \u2014 Slack\/Teams message when a deployment completes or fails<\/li>\n<li><strong>Rollback<\/strong> \u2014 the ability to automatically roll back to the previous version if the new one cannot be loaded<\/li>\n<\/ul>\n<p>Full CI\/CD for Qlik Sense (or any Qlik product) is a topic that gets debated a fair bit, and with the help of the Repository API and the power of QOps, we can confirm that Full CI\/CD is possible. Here is a basic breakdown of what a Full pipeline would typically look like:<\/p>\n<ol>\n<li>Developer commits changes to Git<\/li>\n<li>Pipeline runs: exports the app, runs validation checks<\/li>\n<li>App is deployed to TEST automatically<\/li>\n<li>After manual sign-off, pipeline promotes to PROD<\/li>\n<li>Post-deployment reload verifies the app works<\/li>\n<\/ol>\n<p>This isn&#8217;t theoretical \u2014 teams are running this today.<\/p>\n<h3>10. Plan for Rollback Before You Deploy<\/h3>\n<p>This one is really straightforward. Every time you deploy something to production you should be prepared for the question &#8220;What happens if this breaks production?&#8221; Every deployment should have an answer to this, that is part of the process of doing a deployment in the first place.<\/p>\n<p><strong>Minimum rollback plan:<\/strong><\/p>\n<ul>\n<li>Keep the previous QVF export accessible<\/li>\n<li>Document which version was running before the deployment<\/li>\n<li>Know how to re-import and re-publish the previous version<\/li>\n<li>Know the data connection state of the previous version<\/li>\n<\/ul>\n<p><strong>Better rollback plan:<\/strong><\/p>\n<ul>\n<li>Automated one-click rollback (re-deploy previous version from Git)<\/li>\n<li>Deployment history that shows exactly what was running at any point in time<\/li>\n<li>Instant diff between the broken version and the last working version<\/li>\n<\/ul>\n<hr>\n<p><em>DatalabsUa builds DevOps tools for Qlik analytics. <a href=\"https:\/\/qops.datalabsua.com\">QOps<\/a> brings Git-based version control and CI\/CD automation to Qlik Sense, Qlik Cloud, and QlikView.<\/em><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Are you the type of person who deploys Qlik apps by simply exporting a QVF, renaming it, and then importing it to your target environment? If so you&#8217;re not alone! In fact, in almost every Qlik team we&#8217;ve worked with this is actually a common practice. And in almost every single case, they&#8217;ve had to [&hellip;]<\/p>\n","protected":false},"author":4,"featured_media":45107,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[164,3],"tags":[50,166,168,165,167],"class_list":["post-45089","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-qlik-devops","category-blog","tag-qlik-sense","tag-best-practices","tag-ci-cd","tag-deployment","tag-version-control"],"_links":{"self":[{"href":"https:\/\/datalabsua.com\/en\/wp-json\/wp\/v2\/posts\/45089","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/datalabsua.com\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/datalabsua.com\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/datalabsua.com\/en\/wp-json\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/datalabsua.com\/en\/wp-json\/wp\/v2\/comments?post=45089"}],"version-history":[{"count":4,"href":"https:\/\/datalabsua.com\/en\/wp-json\/wp\/v2\/posts\/45089\/revisions"}],"predecessor-version":[{"id":45104,"href":"https:\/\/datalabsua.com\/en\/wp-json\/wp\/v2\/posts\/45089\/revisions\/45104"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/datalabsua.com\/en\/wp-json\/wp\/v2\/media\/45107"}],"wp:attachment":[{"href":"https:\/\/datalabsua.com\/en\/wp-json\/wp\/v2\/media?parent=45089"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/datalabsua.com\/en\/wp-json\/wp\/v2\/categories?post=45089"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/datalabsua.com\/en\/wp-json\/wp\/v2\/tags?post=45089"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}