<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://www.jbamberger.de/feed.xml" rel="self" type="application/atom+xml" /><link href="https://www.jbamberger.de/" rel="alternate" type="text/html" /><updated>2026-02-21T12:57:10+01:00</updated><id>https://www.jbamberger.de/feed.xml</id><title type="html">Jannik Bamberger</title><subtitle>Website and blog of Jannik Bamberger.</subtitle><entry><title type="html">Generating Android asset files with Gradle</title><link href="https://www.jbamberger.de/development/2021/07/03/android-generating-asset-files.html" rel="alternate" type="text/html" title="Generating Android asset files with Gradle" /><published>2021-07-03T17:00:00+02:00</published><updated>2021-07-03T17:00:00+02:00</updated><id>https://www.jbamberger.de/development/2021/07/03/android-generating-asset-files</id><content type="html" xml:base="https://www.jbamberger.de/development/2021/07/03/android-generating-asset-files.html"><![CDATA[<p>Recently, I’ve come across the problem that I need to include a generated file in the asset files of my Android application. More specifically, the report of a build-time dependency license validation (using the <a href="https://github.com/cashapp/licensee">Licensee</a> Gradle plugin) should be included in the application. Obviously, this should be possible with a flexible build system like Gradle. The solution I came up with has three steps.</p>

<ol>
  <li>Add a new asset directory where we can place the generated asset files.</li>
  <li>Add a task to Gradle that generates the asset files.</li>
  <li>Add dependencies to the Task to ensure that it is called automatically.</li>
</ol>

<p>First we register the output directory of our asset generation task as a new asset directory. This ensures, that its contents are picked up by the <code class="language-plaintext highlighter-rouge">mergeDebugAssets</code> and <code class="language-plaintext highlighter-rouge">mergeReleaseAssets</code> tasks and included in the output apk file.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">sourceSets</span><span class="p">[</span><span class="s">"main"</span><span class="p">].</span><span class="n">assets</span><span class="p">.</span><span class="nf">srcDir</span><span class="p">(</span>
    <span class="n">layout</span><span class="p">.</span><span class="n">buildDirectory</span><span class="p">.</span><span class="nf">dir</span><span class="p">(</span><span class="s">"generated/dependencyAssets/"</span><span class="p">))</span>
</code></pre></div></div>

<p>Next, we need to tell Gradle, how the asset files should be generated. This is done for each build variant independently, because the asset files we generate might differ between variants:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">applicationVariants</span><span class="p">.</span><span class="nf">configureEach</span> <span class="p">{</span>
    <span class="kd">val</span> <span class="py">variant</span> <span class="p">=</span> <span class="k">this</span>

    <span class="c1">// New tasks are configured here.</span>
<span class="p">}</span>
</code></pre></div></div>

<p>For each variant we create a new task to generate the asset files. In my case this generation task simply copies the output of a Gradle plugin that walks the dependency tree and collects all license files.</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">copyArtifactsTask</span> <span class="p">=</span>
    <span class="n">tasks</span><span class="p">.</span><span class="n">register</span><span class="p">&lt;</span><span class="nc">Copy</span><span class="p">&gt;(</span><span class="s">"copy${variant.name.capitalize()}ArtifactList"</span><span class="p">)</span> <span class="p">{</span>
        <span class="nf">from</span><span class="p">(</span>
            <span class="n">project</span><span class="p">.</span><span class="n">extensions</span><span class="p">.</span><span class="nf">getByType</span><span class="p">(</span><span class="nc">ReportingExtension</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">)</span>
                <span class="p">.</span><span class="nf">file</span><span class="p">(</span><span class="s">"licensee/${variant.name}/artifacts.json"</span><span class="p">)</span>
        <span class="p">)</span>
        <span class="nf">into</span><span class="p">(</span><span class="n">layout</span><span class="p">.</span><span class="n">buildDirectory</span><span class="p">.</span><span class="nf">dir</span><span class="p">(</span><span class="s">"generated/dependencyAssets/"</span><span class="p">))</span>
    <span class="p">}</span>
</code></pre></div></div>

<p>For cases where you have more complex asset generation code you can create a task that executes arbitrary Kotlin code, for example as follows:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">val</span> <span class="py">customTask</span> <span class="p">=</span> <span class="n">tasks</span><span class="p">.</span><span class="nf">register</span><span class="p">(</span><span class="s">"generate${variant.name.capitalize()}AssetFiles"</span><span class="p">)</span> <span class="p">{</span>
    <span class="nf">doLast</span> <span class="p">{</span>
        <span class="c1">// Write your code here.</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>Finally, we need to ensure, that the task of ours is actually called before the assets are merged. This can be done with the <code class="language-plaintext highlighter-rouge">dependsOn</code> function of the task class. We want to execute our task prior to the <code class="language-plaintext highlighter-rouge">merge{variant}Assets</code> tasks. This can be done with the following line:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">tasks</span><span class="p">[</span><span class="s">"merge${variant.name.capitalize()}Assets"</span><span class="p">].</span><span class="nf">dependsOn</span><span class="p">(</span><span class="n">copyArtifactsTask</span><span class="p">)</span>
</code></pre></div></div>

<p>If your asset generation depends on some other output you need to declare this dependency, too. In this example, the dependency report needs to be generated prior to copying it, so we need to add a dependency to our task as follows:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">copyArtifactsTask</span><span class="p">.</span><span class="nf">dependsOn</span><span class="p">(</span><span class="s">"licensee${variant.name.capitalize()}"</span><span class="p">)</span>
</code></pre></div></div>

<p>The full example follows:</p>

<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">android</span> <span class="p">{</span>
    
    <span class="c1">// ... Your other build stuff</span>

    <span class="c1">// Register our custom output directory as an asset folder that will be</span>
    <span class="c1">// used for asset merging.</span>
    <span class="n">sourceSets</span><span class="p">[</span><span class="s">"main"</span><span class="p">].</span><span class="n">assets</span><span class="p">.</span><span class="nf">srcDir</span><span class="p">(</span>
        <span class="n">layout</span><span class="p">.</span><span class="n">buildDirectory</span><span class="p">.</span><span class="nf">dir</span><span class="p">(</span><span class="s">"generated/dependencyAssets/"</span><span class="p">))</span>

    <span class="c1">// Individual tasks for release and debug builds</span>
    <span class="n">applicationVariants</span><span class="p">.</span><span class="nf">configureEach</span> <span class="p">{</span>
        <span class="kd">val</span> <span class="py">variant</span> <span class="p">=</span> <span class="k">this</span>

        <span class="c1">// Define a new task to generate the asset files. Here the file is only</span>
        <span class="c1">// copied, but you could do anything else instead.</span>
        <span class="kd">val</span> <span class="py">copyArtifactsTask</span> <span class="p">=</span>
            <span class="n">tasks</span><span class="p">.</span><span class="n">register</span><span class="p">&lt;</span><span class="nc">Copy</span><span class="p">&gt;(</span><span class="s">"copy${variant.name.capitalize()}ArtifactList"</span><span class="p">)</span> <span class="p">{</span>
                <span class="nf">from</span><span class="p">(</span>
                    <span class="n">project</span><span class="p">.</span><span class="n">extensions</span><span class="p">.</span><span class="nf">getByType</span><span class="p">(</span><span class="nc">ReportingExtension</span><span class="o">::</span><span class="k">class</span><span class="p">.</span><span class="n">java</span><span class="p">)</span>
                        <span class="p">.</span><span class="nf">file</span><span class="p">(</span><span class="s">"licensee/${variant.name}/artifacts.json"</span><span class="p">)</span>
                <span class="p">)</span>
                <span class="nf">into</span><span class="p">(</span><span class="n">layout</span><span class="p">.</span><span class="n">buildDirectory</span><span class="p">.</span><span class="nf">dir</span><span class="p">(</span><span class="s">"generated/dependencyAssets/"</span><span class="p">))</span>
            <span class="p">}</span>

        <span class="c1">// This dependency is only necessary if the asset generation depends on</span>
        <span class="c1">// something else, the output of the licensee plugin in my case.</span>
        <span class="n">copyArtifactsTask</span><span class="p">.</span><span class="nf">dependsOn</span><span class="p">(</span><span class="s">"licensee${variant.name.capitalize()}"</span><span class="p">)</span>

        <span class="c1">// Add a dependency between the asset merging and our generation task.</span>
        <span class="c1">// This is necessary to ensure that the assets are generated prior to</span>
        <span class="c1">// the merging step.</span>
        <span class="n">tasks</span><span class="p">[</span><span class="s">"merge${variant.name.capitalize()}Assets"</span><span class="p">].</span><span class="nf">dependsOn</span><span class="p">(</span><span class="n">copyArtifactsTask</span><span class="p">)</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="useful-links">Useful links</h2>

<ul>
  <li>Licensee: <a href="https://github.com/cashapp/licensee">https://github.com/cashapp/licensee</a></li>
</ul>

<!-- References -->]]></content><author><name></name></author><category term="Development" /><category term="Android" /><category term="Gradle" /><category term="Kotlin" /><summary type="html"><![CDATA[Recently, I’ve come across the problem that I need to include a generated file in the asset files of my Android application. More specifically, the report of a build-time dependency license validation (using the Licensee Gradle plugin) should be included in the application. Obviously, this should be possible with a flexible build system like Gradle. The solution I came up with has three steps.]]></summary></entry><entry><title type="html">LetsEncrypt with Certbot and Cloudflare</title><link href="https://www.jbamberger.de/development/2019/01/19/letsencrypt-certbot-cloudflare.html" rel="alternate" type="text/html" title="LetsEncrypt with Certbot and Cloudflare" /><published>2019-01-19T21:13:00+01:00</published><updated>2019-01-19T21:13:00+01:00</updated><id>https://www.jbamberger.de/development/2019/01/19/letsencrypt-certbot-cloudflare</id><content type="html" xml:base="https://www.jbamberger.de/development/2019/01/19/letsencrypt-certbot-cloudflare.html"><![CDATA[<p>This page shows how to configure <a href="https://certbot.eff.org/">Certbot</a> with a domain that uses Cloudflare DNS. This allows us to issue wildcard certificates.</p>

<p>It assumes, that you’ve already set up your server and can establish an SSH connection. My server is running CentOS, so you’ll have to replace <code class="language-plaintext highlighter-rouge">yum</code> with the appropriate package manager for your system, for Ubuntu this it is <code class="language-plaintext highlighter-rouge">apt-get</code>.</p>

<p>If you haven’t installed Certbot go to <a href="https://certbot.eff.org/">https://certbot.eff.org/</a> and follow the instructions.</p>

<h2 id="cloudflare-dns-plugin-for-certbot">Cloudflare-DNS plugin for Certbot</h2>

<p>Next, we’ll need to install the Cloudflare DNS plugin for Certbot:</p>

<p>First, we need the Python package manager PIP.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>yum <span class="nb">install </span>python-pip
</code></pre></div></div>

<p>which we can use to install the Certbot plugin</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>pip <span class="nb">install </span>certbot-dns-cloudflare
</code></pre></div></div>

<h2 id="set-up-the-credentials-file">Set up the credentials file</h2>

<p>Create a new file <code class="language-plaintext highlighter-rouge">cloudflare.ini</code> in <code class="language-plaintext highlighter-rouge">~/.secrets/certbot/cloudflare.ini</code> and write your Cloudflare account credentials down as follows:</p>

<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Cloudflare API credentials used by Certbot
</span><span class="py">dns_cloudflare_email</span> <span class="p">=</span> <span class="s">&lt;your-email-address&gt;</span>
<span class="py">dns_cloudflare_api_key</span> <span class="p">=</span> <span class="s">&lt;your-api-key&gt;</span>
</code></pre></div></div>

<p>You can find your Cloudflare API key by logging into Cloudflare, and then going to <em>My Profile</em>. There you can copy the <em>Global API Key</em>.</p>

<p>Because these credentials can be used to perform arbitrary actions on your Cloudflare account, it is essential to set the correct access permissions. You can restrict the access to your account by running <code class="language-plaintext highlighter-rouge">chmod 600 ~/.secrets/certbot/cloudflare.ini</code>. This will also remove write permission for you, so make sure to finish editing beforehand.</p>

<h2 id="issuing-the-certificate">Issuing the certificate</h2>

<p>If you are using Certbot / LetsEncrypt for the first time, you’ll be asked to create an account in the next step. Just follow the steps and everything should work.</p>

<p>To issue the certificate one command is sufficient:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>certbot certonly <span class="se">\</span>
    <span class="nt">--dns-cloudflare</span> <span class="se">\</span>
    <span class="nt">--dns-cloudflare-credentials</span> ~/.secrets/certbot/cloudflare.ini <span class="se">\</span>
    <span class="nt">--preferred-challenges</span> dns-01 <span class="se">\</span>
    <span class="nt">-d</span> &lt;cert-names&gt;
</code></pre></div></div>

<p>Replace the last line with all names you want to be included in the certificate. You can separate multiple entries with <code class="language-plaintext highlighter-rouge">,</code>.</p>

<p>Example: <code class="language-plaintext highlighter-rouge">example.com, www.example.com, something.example.com</code></p>

<p>A wildcard can be used by including an <code class="language-plaintext highlighter-rouge">*</code> in the name: <code class="language-plaintext highlighter-rouge">*.example.com</code>.</p>

<p>The active certificate will be linked into <code class="language-plaintext highlighter-rouge">/etc/letsencrypt/live</code>. You can now configure your server to use the certificate.</p>

<h2 id="automatic-renewal">Automatic renewal</h2>

<p>Because LetsEncrypt certificates are valid for only 90 days, it is a good idea to schedule the certificate renewal automatically, such that there is always a valid certificate available.
This can be done for example by creating a cron job:</p>

<p>Run <code class="language-plaintext highlighter-rouge">crontab -e</code> and then enter the following line:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0 <span class="k">*</span>/12 <span class="k">*</span> <span class="k">*</span> <span class="k">*</span> root <span class="nb">test</span> <span class="nt">-x</span> /usr/bin/certbot <span class="nt">-a</span> <span class="se">\!</span> <span class="nt">-d</span> /run/systemd/system <span class="o">&amp;&amp;</span> perl <span class="nt">-e</span> <span class="s1">'sleep int(rand(3600))'</span> <span class="o">&amp;&amp;</span> certbot <span class="nt">-q</span> renew
</code></pre></div></div>

<h2 id="useful-links">Useful links</h2>

<ul>
  <li>LetsEncrypt: <a href="https://letsencrypt.org/">https://letsencrypt.org/</a></li>
  <li>Certbot: <a href="https://certbot.eff.org/">https://certbot.eff.org/</a></li>
  <li>Cloudflare DNS plugin: <a href="https://certbot-dns-cloudflare.readthedocs.io/en/stable/">https://certbot-dns-cloudflare.readthedocs.io/en/stable/</a></li>
  <li>Test your SSL configuration: <a href="https://www.ssllabs.com/ssltest/">https://www.ssllabs.com/ssltest/</a></li>
</ul>]]></content><author><name></name></author><category term="Development" /><category term="Lets-Encrypt" /><category term="Certbot" /><category term="Cloudflare" /><summary type="html"><![CDATA[This page shows how to configure Certbot with a domain that uses Cloudflare DNS. This allows us to issue wildcard certificates.]]></summary></entry><entry><title type="html">Spring Boot deployment with nginx</title><link href="https://www.jbamberger.de/development/2018/12/29/springboot-deployment-with-nginx.html" rel="alternate" type="text/html" title="Spring Boot deployment with nginx" /><published>2018-12-29T15:05:00+01:00</published><updated>2018-12-29T15:05:00+01:00</updated><id>https://www.jbamberger.de/development/2018/12/29/springboot-deployment-with-nginx</id><content type="html" xml:base="https://www.jbamberger.de/development/2018/12/29/springboot-deployment-with-nginx.html"><![CDATA[<p>This post will show how to configure a CentOS server for deploying a Spring Boot
application build with Gradle. The networking is passed trough nginx which can
be used to run multiple servers on the same host or serve static traffic faster.</p>

<h2 id="configure-nginx">Configure nginx</h2>

<p>To configure nginx add the following server declaration to your
<code class="language-plaintext highlighter-rouge">/etc/nginx/conf.d/nginx.conf</code> file.</p>

<div class="language-nginx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">server</span> <span class="p">{</span>
        <span class="kn">listen</span> <span class="mi">80</span><span class="p">;</span>
        <span class="kn">listen</span> <span class="s">[::]:80</span><span class="p">;</span>

        <span class="kn">server_name</span> <span class="s">&lt;your-domain&gt;</span><span class="p">;</span>

        <span class="kn">location</span> <span class="n">/</span> <span class="p">{</span>
             <span class="kn">proxy_pass</span> <span class="s">http://localhost:8080/</span><span class="p">;</span>
             <span class="kn">proxy_set_header</span> <span class="s">X-Forwarded-For</span> <span class="nv">$proxy_add_x_forwarded_for</span><span class="p">;</span>
             <span class="kn">proxy_set_header</span> <span class="s">X-Forwarded-Proto</span> <span class="nv">$scheme</span><span class="p">;</span>
             <span class="kn">proxy_set_header</span> <span class="s">X-Forwarded-Port</span> <span class="nv">$server_port</span><span class="p">;</span>
        <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>This will pass the traffic through to the locally running app server.</p>

<p>Then test the configuration and restart nginx.</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>nginx <span class="nt">-t</span> <span class="c"># test the new configuration for valid syntax</span>
<span class="nb">sudo </span>systemctl restart nginx
</code></pre></div></div>

<h2 id="configure-deployment-in-gradle">Configure deployment in Gradle</h2>

<p>First off, we need to define the server information and credentials. Log into
the server and create a user <code class="language-plaintext highlighter-rouge">deployment</code>. Assign the <code class="language-plaintext highlighter-rouge">sudo</code> privilege to the
newly created user. Then generate a SSH key <code class="language-plaintext highlighter-rouge">deployment</code> locally and add it to
the allowed keys on the server.</p>

<p>Edit the user <code class="language-plaintext highlighter-rouge">gradle.properties</code> file at <code class="language-plaintext highlighter-rouge">~/.gradle/gradle.properties</code> (or
create it if it doesn’t exist) and add the following:</p>

<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">server_key_deployment_passphrase</span>=&lt;<span class="n">deployment</span>-<span class="n">ssh</span>-<span class="n">key</span>-<span class="n">passphrase</span>&gt;
<span class="n">server_user_deployment_passphrase</span>=&lt;<span class="n">deployment</span>-<span class="n">user</span>-<span class="n">passphrase</span>&gt;
</code></pre></div></div>

<p>Now we need to set up Gradle to deploy the application. To do so we add the
<code class="language-plaintext highlighter-rouge">org.hidetake.ssh</code> plugin which allows us to perform SSH operations:</p>

<div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">plugins</span> <span class="o">{</span>
    <span class="n">id</span> <span class="s1">'org.hidetake.ssh'</span> <span class="n">version</span> <span class="s1">'2.9.0'</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Then we configure our server access properties:</p>

<div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">remotes</span> <span class="o">{</span>
    <span class="n">webServer</span> <span class="o">{</span>
        <span class="n">host</span> <span class="o">=</span> <span class="s1">'&lt;your-ip&gt;'</span>
        <span class="n">user</span> <span class="o">=</span> <span class="s1">'deployment'</span>
        <span class="n">port</span> <span class="o">=</span> <span class="o">&lt;</span><span class="n">your</span><span class="o">-</span><span class="n">ssh</span><span class="o">-</span><span class="n">port</span><span class="o">&gt;</span>
        <span class="n">identity</span> <span class="o">=</span> <span class="n">file</span><span class="o">(</span><span class="s2">"${System.properties['user.home']}/.ssh/deployment"</span><span class="o">)</span>
        <span class="n">passphrase</span> <span class="o">=</span> <span class="n">server_key_deployment_passphrase</span>
        <span class="n">sudoPassword</span> <span class="o">=</span> <span class="n">server_user_deployment_passphrase</span>
        <span class="n">errorStream</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">err</span>
        <span class="n">outputStream</span> <span class="o">=</span> <span class="n">System</span><span class="o">.</span><span class="na">out</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Note how the passphrases are loaded from the properties file, such that they do
not end up in version control by accident.</p>

<p>Next we define the build and deployment directories:</p>

<div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">def</span> <span class="n">remote_dir</span> <span class="o">=</span> <span class="s1">'/var/www/&lt;yourapp&gt;'</span>        <span class="c1">// server deployment location</span>
<span class="kt">def</span> <span class="n">local_dir</span> <span class="o">=</span> <span class="s1">'build/libs'</span>                 <span class="c1">// local artefact output directory</span>
<span class="kt">def</span> <span class="n">deploy_file</span> <span class="o">=</span> <span class="s2">"&lt;yourapp&gt;-${version}.jar"</span> <span class="c1">// artefact name</span>
</code></pre></div></div>

<p>Now we add a new task to perform the deployment. The task requires that the
artefact is build and checked (i.e. test cases are ran). Then it connects to the
server and pushes the newly built file. The deployed application versions exist
all side by side. The currently active one is linked to <code class="language-plaintext highlighter-rouge">&lt;yourapp&gt;.jar</code>. Then
the app server is restarted.</p>

<div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">task</span><span class="o">(</span><span class="s1">'deploy'</span><span class="o">)</span> <span class="o">{</span>
  <span class="n">dependsOn</span> <span class="n">build</span><span class="o">,</span> <span class="n">test</span><span class="o">,</span> <span class="n">check</span>
  <span class="n">doLast</span> <span class="o">{</span>
    <span class="n">ssh</span><span class="o">.</span><span class="na">run</span> <span class="o">{</span>
      <span class="n">session</span><span class="o">(</span><span class="n">remotes</span><span class="o">.</span><span class="na">webServer</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">put</span> <span class="nl">from:</span> <span class="s2">"${projectDir}/${local_dir}/${deploy_file}"</span><span class="o">,</span> <span class="nl">into:</span> <span class="n">remote_dir</span>
        <span class="n">execute</span> <span class="s2">"ln -sf ${remote_dir}/${deploy_file} ${remote_dir}/&lt;yourapp&gt;.jar"</span>
        <span class="n">executeSudo</span><span class="o">(</span><span class="s1">'systemctl restart &lt;yourapp&gt;.service'</span><span class="o">,</span> <span class="nl">pty:</span> <span class="kc">true</span><span class="o">)</span>
      <span class="o">}</span>
    <span class="o">}</span>
  <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<h2 id="register-the-application-with-systemd">Register the application with systemd</h2>

<p>The new application needs to be registered with systemd such that it can be
controlled easily. To do so, create a unit file at
<code class="language-plaintext highlighter-rouge">/etc/systemd/system/&lt;yourapp&gt;.service</code> and write the following content inside.</p>

<div class="language-config highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[<span class="n">Unit</span>]
<span class="n">Description</span>=<span class="n">Spring</span> <span class="n">Boot</span> <span class="n">Application</span>
<span class="n">After</span>=<span class="n">syslog</span>.<span class="n">target</span>
<span class="n">After</span>=<span class="n">network</span>.<span class="n">target</span>[<span class="n">Service</span>]
<span class="n">User</span>=&lt;<span class="n">serveruser</span>&gt;
<span class="n">Type</span>=<span class="n">simple</span>

[<span class="n">Service</span>]
<span class="n">ExecStart</span>=/<span class="n">usr</span>/<span class="n">bin</span>/<span class="n">java</span> -<span class="n">jar</span> /<span class="n">var</span>/<span class="n">www</span>/&lt;<span class="n">yourapp</span>&gt;/&lt;<span class="n">yourapp</span>&gt;.<span class="n">jar</span>
<span class="n">Restart</span>=<span class="n">always</span>
<span class="n">StandardOutput</span>=<span class="n">syslog</span>
<span class="n">StandardError</span>=<span class="n">syslog</span>
<span class="n">SyslogIdentifier</span>=&lt;<span class="n">yourapp</span>&gt;

[<span class="n">Install</span>]
<span class="n">WantedBy</span>=<span class="n">multi</span>-<span class="n">user</span>.<span class="n">target</span>
</code></pre></div></div>

<p>Then start the app server:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>systemctl start &lt;yourapp&gt;
<span class="nb">sudo </span>systemctl status &lt;yourapp&gt;
</code></pre></div></div>

<h2 id="perform-a-local-test">Perform a local test</h2>

<p>Build the uber-jar containing all of the necessary jars, including the app
server (e.g. Tomcat) by invoking</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./gradlew build
</code></pre></div></div>

<p>Now run it with <code class="language-plaintext highlighter-rouge">java -jar &lt;yourapp&gt;.jar</code> and test it.</p>

<h2 id="perform-the-deployment">Perform the deployment</h2>

<p>To perform a deployment to the server you need to invoke the new Gradle task <code class="language-plaintext highlighter-rouge">deploy</code>:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./gradlew deploy
</code></pre></div></div>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://www.linode.com/docs/development/java/how-to-deploy-spring-boot-applications-nginx-ubuntu-16-04/">Deploy Spring Boot Applications with an NGINX Reverse Proxy</a></li>
</ul>]]></content><author><name></name></author><category term="Development" /><category term="Java" /><category term="Spring" /><category term="Boot" /><category term="Gradle" /><category term="Nginx" /><category term="CentOS" /><category term="systemd" /><summary type="html"><![CDATA[This post will show how to configure a CentOS server for deploying a Spring Boot application build with Gradle. The networking is passed trough nginx which can be used to run multiple servers on the same host or serve static traffic faster.]]></summary></entry><entry><title type="html">Android + Gradle + Artifactory</title><link href="https://www.jbamberger.de/development/2018/05/18/android-gradle-artifactory.html" rel="alternate" type="text/html" title="Android + Gradle + Artifactory" /><published>2018-05-18T21:44:00+02:00</published><updated>2018-05-18T21:44:00+02:00</updated><id>https://www.jbamberger.de/development/2018/05/18/android-gradle-artifactory</id><content type="html" xml:base="https://www.jbamberger.de/development/2018/05/18/android-gradle-artifactory.html"><![CDATA[<p>This page shows how to integrate Gradle builds of Android libraries with a private Maven repository hosted on Artifactory.</p>

<p>You’ll need:</p>

<ol>
  <li>The library identifier:
 <code class="language-plaintext highlighter-rouge">&lt;your-group-id&gt;:&lt;your-artifact-id&gt;:&lt;your-version-number&gt;</code>
 example: <code class="language-plaintext highlighter-rouge">com.example.test:mylibrary:1.0.0</code></li>
  <li>An Artifactory server with a repository:
 <code class="language-plaintext highlighter-rouge">https://&lt;your-server&gt;/&lt;your-base-path&gt;/&lt;your-repository&gt;</code>
 example: <code class="language-plaintext highlighter-rouge">https://example.com/artifactory/gradle-release-local</code></li>
  <li>A user account with sufficient access rights consisting of <code class="language-plaintext highlighter-rouge">&lt;your-username&gt;</code> and <code class="language-plaintext highlighter-rouge">&lt;your-password&gt;</code>.</li>
</ol>

<h2 id="producer-and-consumer">Producer and Consumer</h2>

<p>Both, producer and consumer must create a properties file containing the Artifactory username and password.
The file should be created as <code class="language-plaintext highlighter-rouge">~/.gradle/gradle.properties</code>.
The content should be:</p>

<div class="language-ini highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="py">artifactory_username</span><span class="p">=</span><span class="s">&lt;your-username&gt;</span>
<span class="py">artifactory_password</span><span class="p">=</span><span class="s">&lt;your-password&gt;</span>
</code></pre></div></div>

<h2 id="producer-side">Producer side</h2>

<p>Project level <code class="language-plaintext highlighter-rouge">build.gradle</code> file:</p>

<div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">buildscript</span> <span class="o">{</span>
    <span class="n">dependencies</span> <span class="o">{</span>
        <span class="n">classpath</span> <span class="s2">"org.jfrog.buildinfo:build-info-extractor-gradle:4.7.1"</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Module level <code class="language-plaintext highlighter-rouge">build.gradle</code> file:</p>

<div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">apply</span> <span class="nl">plugin:</span> <span class="s1">'com.jfrog.artifactory'</span>
<span class="n">apply</span> <span class="nl">plugin:</span> <span class="s1">'maven-publish'</span>
<span class="n">apply</span> <span class="nl">plugin:</span> <span class="s1">'com.android.library'</span>

<span class="c1">// define name and version</span>
<span class="kt">def</span> <span class="n">libraryGroupId</span> <span class="o">=</span> <span class="s1">'&lt;your-group-id&gt;'</span>
<span class="kt">def</span> <span class="n">libraryArtifactId</span> <span class="o">=</span> <span class="s1">'&lt;your-artifact-id&gt;'</span>
<span class="kt">def</span> <span class="n">libraryVersion</span> <span class="o">=</span> <span class="s1">'&lt;your-version-number&gt;'</span>

<span class="c1">// add the android and dependencies sections here...</span>

<span class="n">publishing</span> <span class="o">{</span>
    <span class="n">publications</span> <span class="o">{</span>
        <span class="n">aar</span><span class="o">(</span><span class="n">MavenPublication</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">groupId</span> <span class="n">libraryGroupId</span>
            <span class="n">version</span> <span class="n">id</span><span class="o">.</span><span class="na">libraryVersion</span>
            <span class="n">artifactId</span> <span class="n">libraryArtifactId</span>

            <span class="nf">artifact</span><span class="o">(</span><span class="s2">"$buildDir/outputs/aar/${artifactId}-release.aar"</span><span class="o">)</span>

            <span class="c1">// Build a pom file which will be published, so the consumer does not</span>
            <span class="c1">// need to declare all dependencies manually.</span>
            <span class="n">pom</span><span class="o">.</span><span class="na">withXml</span> <span class="o">{</span>
                <span class="kd">final</span> <span class="n">dependenciesNode</span> <span class="o">=</span> <span class="n">asNode</span><span class="o">().</span><span class="na">appendNode</span><span class="o">(</span><span class="s1">'dependencies'</span><span class="o">)</span>

                <span class="n">ext</span><span class="o">.</span><span class="na">addDependency</span> <span class="o">=</span> <span class="o">{</span> <span class="n">Dependency</span> <span class="n">dep</span><span class="o">,</span> <span class="n">String</span> <span class="n">scope</span> <span class="o">-&gt;</span>
                    <span class="k">if</span> <span class="o">(</span><span class="n">dep</span><span class="o">.</span><span class="na">group</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="n">dep</span><span class="o">.</span><span class="na">version</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="n">dep</span><span class="o">.</span><span class="na">name</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="n">dep</span><span class="o">.</span><span class="na">name</span> <span class="o">==</span> <span class="s2">"unspecified"</span><span class="o">)</span>
                        <span class="k">return</span> <span class="c1">// ignore invalid dependencies</span>

                    <span class="kd">final</span> <span class="n">dependencyNode</span> <span class="o">=</span> <span class="n">dependenciesNode</span><span class="o">.</span><span class="na">appendNode</span><span class="o">(</span><span class="s1">'dependency'</span><span class="o">)</span>
                    <span class="n">dependencyNode</span><span class="o">.</span><span class="na">appendNode</span><span class="o">(</span><span class="s1">'groupId'</span><span class="o">,</span> <span class="n">dep</span><span class="o">.</span><span class="na">group</span><span class="o">)</span>
                    <span class="n">dependencyNode</span><span class="o">.</span><span class="na">appendNode</span><span class="o">(</span><span class="s1">'artifactId'</span><span class="o">,</span> <span class="n">dep</span><span class="o">.</span><span class="na">name</span><span class="o">)</span>
                    <span class="n">dependencyNode</span><span class="o">.</span><span class="na">appendNode</span><span class="o">(</span><span class="s1">'version'</span><span class="o">,</span> <span class="n">dep</span><span class="o">.</span><span class="na">version</span><span class="o">)</span>
                    <span class="n">dependencyNode</span><span class="o">.</span><span class="na">appendNode</span><span class="o">(</span><span class="s1">'scope'</span><span class="o">,</span> <span class="n">scope</span><span class="o">)</span>

                    <span class="k">if</span> <span class="o">(!</span><span class="n">dep</span><span class="o">.</span><span class="na">transitive</span><span class="o">)</span> <span class="o">{</span>
                        <span class="c1">// If this dependency is transitive, we should force exclude</span>
                        <span class="c1">// all of its dependencies from the POM</span>
                        <span class="kd">final</span> <span class="n">exclusionNode</span> <span class="o">=</span> <span class="n">dependencyNode</span><span class="o">.</span><span class="na">appendNode</span><span class="o">(</span><span class="s1">'exclusions'</span><span class="o">).</span><span class="na">appendNode</span><span class="o">(</span><span class="s1">'exclusion'</span><span class="o">)</span>
                        <span class="n">exclusionNode</span><span class="o">.</span><span class="na">appendNode</span><span class="o">(</span><span class="s1">'groupId'</span><span class="o">,</span> <span class="s1">'*'</span><span class="o">)</span>
                        <span class="n">exclusionNode</span><span class="o">.</span><span class="na">appendNode</span><span class="o">(</span><span class="s1">'artifactId'</span><span class="o">,</span> <span class="s1">'*'</span><span class="o">)</span>
                    <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(!</span><span class="n">dep</span><span class="o">.</span><span class="na">properties</span><span class="o">.</span><span class="na">excludeRules</span><span class="o">.</span><span class="na">empty</span><span class="o">)</span> <span class="o">{</span>
                        <span class="c1">// Otherwise add specified exclude rules</span>
                        <span class="kd">final</span> <span class="n">exclusionNode</span> <span class="o">=</span> <span class="n">dependencyNode</span><span class="o">.</span><span class="na">appendNode</span><span class="o">(</span><span class="s1">'exclusions'</span><span class="o">).</span><span class="na">appendNode</span><span class="o">(</span><span class="s1">'exclusion'</span><span class="o">)</span>
                        <span class="n">dep</span><span class="o">.</span><span class="na">properties</span><span class="o">.</span><span class="na">excludeRules</span><span class="o">.</span><span class="na">each</span> <span class="o">{</span> <span class="n">ExcludeRule</span> <span class="n">rule</span> <span class="o">-&gt;</span>
                            <span class="n">exclusionNode</span><span class="o">.</span><span class="na">appendNode</span><span class="o">(</span><span class="s1">'groupId'</span><span class="o">,</span> <span class="n">rule</span><span class="o">.</span><span class="na">group</span> <span class="o">?:</span> <span class="s1">'*'</span><span class="o">)</span>
                            <span class="n">exclusionNode</span><span class="o">.</span><span class="na">appendNode</span><span class="o">(</span><span class="s1">'artifactId'</span><span class="o">,</span> <span class="n">rule</span><span class="o">.</span><span class="na">module</span> <span class="o">?:</span> <span class="s1">'*'</span><span class="o">)</span>
                        <span class="o">}</span>
                    <span class="o">}</span>
                <span class="o">}</span>
                <span class="c1">// Map all gradle dependencies to maven dependencies (the compile dependency of gradle is only</span>
                <span class="c1">// present to support legacy gradle build files)</span>
                <span class="n">configurations</span><span class="o">.</span><span class="na">compile</span>       <span class="o">.</span><span class="na">getAllDependencies</span><span class="o">().</span><span class="na">each</span> <span class="o">{</span> <span class="n">dep</span> <span class="o">-&gt;</span> <span class="n">addDependency</span><span class="o">(</span><span class="n">dep</span><span class="o">,</span> <span class="s2">"compile"</span><span class="o">)</span> <span class="o">}</span>
                <span class="n">configurations</span><span class="o">.</span><span class="na">api</span>           <span class="o">.</span><span class="na">getAllDependencies</span><span class="o">().</span><span class="na">each</span> <span class="o">{</span> <span class="n">dep</span> <span class="o">-&gt;</span> <span class="n">addDependency</span><span class="o">(</span><span class="n">dep</span><span class="o">,</span> <span class="s2">"compile"</span><span class="o">)</span> <span class="o">}</span>
                <span class="n">configurations</span><span class="o">.</span><span class="na">implementation</span><span class="o">.</span><span class="na">getAllDependencies</span><span class="o">().</span><span class="na">each</span> <span class="o">{</span> <span class="n">dep</span> <span class="o">-&gt;</span> <span class="n">addDependency</span><span class="o">(</span><span class="n">dep</span><span class="o">,</span> <span class="s2">"runtime"</span><span class="o">)</span> <span class="o">}</span>
            <span class="o">}</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>

<span class="n">artifactory</span> <span class="o">{</span>
    <span class="n">contextUrl</span> <span class="o">=</span> <span class="s1">'https://&lt;your-server&gt;/&lt;your-base-path&gt;'</span>
    <span class="n">publish</span> <span class="o">{</span>
        <span class="n">repository</span> <span class="o">{</span>
            <span class="n">repoKey</span> <span class="o">=</span> <span class="s1">'&lt;your-repository&gt;'</span>

            <span class="n">username</span> <span class="o">=</span> <span class="n">artifactory_username</span>
            <span class="n">password</span> <span class="o">=</span> <span class="n">artifactory_password</span>
        <span class="o">}</span>
        <span class="n">defaults</span> <span class="o">{</span>
            <span class="n">publications</span><span class="o">(</span><span class="s1">'aar'</span><span class="o">)</span>
            <span class="n">publishArtifacts</span> <span class="o">=</span> <span class="kc">true</span>

            <span class="n">properties</span> <span class="o">=</span> <span class="o">[</span>
                <span class="s1">'qa.level'</span><span class="o">:</span> <span class="s1">'basic'</span><span class="o">,</span>
                <span class="s1">'q.os'</span>    <span class="o">:</span> <span class="s1">'android'</span><span class="o">,</span>
                <span class="s1">'dev.team'</span><span class="o">:</span> <span class="s1">'core'</span><span class="o">]</span>
            <span class="n">publishPom</span> <span class="o">=</span> <span class="kc">true</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Deploying the artifact is only a matter of invoking <code class="language-plaintext highlighter-rouge">gradlew assembleRelease artifactoryPublish</code>.</p>

<h2 id="consumer-side">Consumer side</h2>

<p>Add the repository to the <code class="language-plaintext highlighter-rouge">repositories</code> section:</p>

<div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">repositories</span> <span class="o">{</span>
    <span class="n">google</span><span class="o">()</span>
    <span class="n">jcenter</span><span class="o">()</span>
    <span class="n">maven</span> <span class="o">{</span>
        <span class="n">url</span> <span class="s2">"https://&lt;your-server&gt;:443/&lt;your-base-path&gt;/&lt;your-repository&gt;"</span>
        <span class="n">credentials</span> <span class="o">{</span>
            <span class="n">username</span> <span class="o">=</span> <span class="s2">"${artifactory_username}"</span>
            <span class="n">password</span> <span class="o">=</span> <span class="s2">"${artifactory_password}"</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>Add the library with its given Maven coordinates to the <code class="language-plaintext highlighter-rouge">dependencies</code> section:</p>

<div class="language-groovy highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">dependencies</span> <span class="o">{</span>
    <span class="n">implementation</span> <span class="s1">'&lt;your-group-id&gt;:&lt;your-artifact-id&gt;:&lt;your-version-number&gt;'</span>
<span class="o">}</span>
</code></pre></div></div>]]></content><author><name></name></author><category term="Development" /><category term="Android" /><category term="Gradle" /><category term="Artifactory" /><summary type="html"><![CDATA[This page shows how to integrate Gradle builds of Android libraries with a private Maven repository hosted on Artifactory.]]></summary></entry><entry><title type="html">Website with Flask, Gunicorn and nginx on CentOS</title><link href="https://www.jbamberger.de/development/2018/01/22/website-with-flask-gunicorn-nginx.html" rel="alternate" type="text/html" title="Website with Flask, Gunicorn and nginx on CentOS" /><published>2018-01-22T12:35:00+01:00</published><updated>2018-01-22T12:35:00+01:00</updated><id>https://www.jbamberger.de/development/2018/01/22/website-with-flask-gunicorn-nginx</id><content type="html" xml:base="https://www.jbamberger.de/development/2018/01/22/website-with-flask-gunicorn-nginx.html"><![CDATA[<p>This page contains info on the setup of a Flask application with Gunicorn as app
server and nginx as webserver. The app will run with Python 3 on CentOS 7.</p>

<h2 id="app-setup">App setup</h2>

<p>Install Python 3 with the following commands:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>yum <span class="nt">-y</span> update
<span class="nb">sudo </span>yum <span class="nt">-y</span> <span class="nb">install </span>yum-utils
<span class="nb">sudo </span>yum <span class="nt">-y</span> groupinstall development
<span class="nb">sudo </span>yum <span class="nt">-y</span> <span class="nb">install </span>https://centos7.iuscommunity.org/ius-release.rpm <span class="c"># community repo</span>
<span class="nb">sudo </span>yum <span class="nt">-y</span> <span class="nb">install </span>python36u <span class="c"># use whatever version fits your needs</span>
<span class="nb">sudo </span>yum <span class="nt">-y</span> <span class="nb">install </span>python36u-pip
<span class="nb">sudo </span>yum <span class="nt">-y</span> <span class="nb">install </span>python36u-devel
</code></pre></div></div>

<p>Create a virtual environment and install dependencies as well as Gunicorn:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>python3.6 <span class="nt">-m</span> venv &lt;appdir-name&gt;
<span class="nb">source</span> &lt;appdir-name&gt;/bin/activate <span class="c"># enable the environment</span>
<span class="c"># if you have requirements.txt use it, otherwise install the dependencies manually</span>
pip <span class="nb">install</span> <span class="nt">-r</span> &lt;appdir-name&gt;/requirements.txt
pip <span class="nb">install </span>gunicorn
deactivate <span class="c"># quit the virtual environment</span>
</code></pre></div></div>

<h2 id="configure-nginx">Configure Nginx</h2>

<p>Edit your nginx configuration (most likely in <code class="language-plaintext highlighter-rouge">/etc/nginx/conf.d/nginx.conf</code>)
and add the following server:</p>

<div class="language-nginx highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">server</span> <span class="p">{</span>
    <span class="kn">listen</span> <span class="mi">80</span><span class="p">;</span>
    <span class="kn">client_max_body_size</span> <span class="mi">4G</span><span class="p">;</span>

    <span class="kn">server_name</span> <span class="s">&lt;your-domain&gt;</span> <span class="s">www.&lt;your-domain&gt;</span><span class="p">;</span>

    <span class="kn">keepalive_timeout</span> <span class="mi">5</span><span class="p">;</span>

    <span class="kn">root</span> <span class="s">&lt;path-to-app&gt;/static</span><span class="p">;</span> <span class="c1"># path to wherever you keep static files</span>

    <span class="kn">location</span> <span class="n">/</span> <span class="p">{</span>
        <span class="c1"># serve static files directly and use app as fallback if not found</span>
        <span class="kn">try_files</span> <span class="nv">$uri</span> <span class="s">@proxy_to_app</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="kn">location</span> <span class="s">@proxy_to_app</span> <span class="p">{</span>
        <span class="c1"># setup reverse proxy to gunicorn</span>
        <span class="kn">proxy_set_header</span> <span class="s">X-Forwarded-For</span> <span class="nv">$proxy_add_x_forwarded_for</span><span class="p">;</span>
        <span class="kn">proxy_set_header</span> <span class="s">X-Forwarded-Proto</span> <span class="nv">$scheme</span><span class="p">;</span>
        <span class="kn">proxy_set_header</span> <span class="s">Host</span> <span class="nv">$http_host</span><span class="p">;</span>
        <span class="kn">proxy_redirect</span> <span class="no">off</span><span class="p">;</span>
        <span class="kn">proxy_pass</span> <span class="s">http://unix:/run/gunicorns/socket</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="c1"># serve error pages from static files</span>
    <span class="kn">error_page</span> <span class="mi">500</span> <span class="mi">502</span> <span class="mi">503</span> <span class="mi">504</span> <span class="n">/500.html</span><span class="p">;</span>
    <span class="kn">location</span> <span class="p">=</span> <span class="n">/500.html</span> <span class="p">{</span>
        <span class="kn">root</span> <span class="s">&lt;path-to-app&gt;/static</span><span class="p">;</span>
    <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="configure-systemd">Configure systemd</h2>

<p>Create a new systemd socket file at <code class="language-plaintext highlighter-rouge">/etc/systemd/system/gunicorn.socket</code> with
the following content.</p>

<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[<span class="n">Unit</span>]
<span class="n">Desctiption</span>=<span class="n">gunicorn</span> <span class="n">socket</span>

[<span class="n">Socket</span>]
<span class="n">ListenStream</span>=/<span class="n">run</span>/<span class="n">gunicorns</span>/<span class="n">socket</span>

[<span class="n">Install</span>]
<span class="n">WantedBy</span>=<span class="n">sockets</span>.<span class="n">target</span>
</code></pre></div></div>

<p>Now create the systemd service file at <code class="language-plaintext highlighter-rouge">/etc/systemd/system/gunicorn.service</code>
and add the following content:</p>

<div class="language-conf highlighter-rouge"><div class="highlight"><pre class="highlight"><code>[<span class="n">Unit</span>]
<span class="n">Description</span>=<span class="n">gunicorn</span> <span class="n">daemon</span>
<span class="n">Requires</span>=<span class="n">gunicorn</span>.<span class="n">socket</span>
<span class="n">After</span>=<span class="n">network</span>.<span class="n">target</span>

[<span class="n">Service</span>]
<span class="n">PIDFile</span>=/<span class="n">run</span>/<span class="n">gunicorn</span>/<span class="n">pid</span>
<span class="n">User</span>=&lt;<span class="n">server</span>-<span class="n">user</span>&gt;
<span class="n">Group</span>=&lt;<span class="n">server</span>-<span class="n">group</span>&gt;
<span class="n">RuntimeDirectory</span>=<span class="n">gunicorn</span>
<span class="n">WorkingDirectory</span>=&lt;<span class="n">path</span>-<span class="n">to</span>-<span class="n">app</span>&gt;
<span class="n">ExecStart</span>=&lt;<span class="n">path</span>-<span class="n">to</span>-<span class="n">app</span>&gt;/<span class="n">bin</span>/<span class="n">gunicorn</span>  <span class="n">app</span>:<span class="n">app</span>  \
        --<span class="n">pid</span> /<span class="n">run</span>/<span class="n">gunicorn</span>/<span class="n">pid</span>   \
        --<span class="n">bind</span> <span class="n">unix</span>:/<span class="n">run</span>/<span class="n">gunicorns</span>/<span class="n">socket</span>
<span class="n">ExecReload</span>=/<span class="n">bin</span>/<span class="n">kill</span> -<span class="n">s</span> <span class="n">HUP</span> $<span class="n">MAINPID</span>
<span class="n">ExecStop</span>=/<span class="n">bin</span>/<span class="n">kill</span> -<span class="n">s</span> <span class="n">TERM</span> $<span class="n">MAINPID</span>
<span class="n">PrivateTmp</span>=<span class="n">true</span>

[<span class="n">Install</span>]
<span class="n">WantedBy</span>=<span class="n">multi</span>-<span class="n">user</span>.<span class="n">target</span>
</code></pre></div></div>

<p>Start and enable Gunicorn:</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>systemctl start gunicorn.socket
<span class="nb">sudo </span>systemctl <span class="nb">enable </span>gunicorn.socket
</code></pre></div></div>

<h2 id="gunicorn-logs">Gunicorn logs</h2>

<p>You can display the logs with <code class="language-plaintext highlighter-rouge">sudo journalctl -u gunicorn</code>.</p>

<h2 id="references">References</h2>

<ul>
  <li><a href="https://www.digitalocean.com/community/tutorials/how-to-install-python-3-and-set-up-a-local-programming-environment-on-centos-7">How To Install Python 3 and Set Up a Local Programming Environment on CentOS 7</a></li>
  <li><a href="https://www.digitalocean.com/community/tutorials/how-to-set-up-django-with-postgres-nginx-and-gunicorn-on-ubuntu-18-04">How To Set Up Django with Postgres, Nginx, and Gunicorn on Ubuntu 18.04</a></li>
  <li><a href="https://docs.gunicorn.org/en/stable/deploy.html">Deploying Gunicorn</a></li>
</ul>]]></content><author><name></name></author><category term="Development" /><category term="Python" /><category term="Flask" /><category term="Gunicorn" /><category term="Nginx" /><summary type="html"><![CDATA[This page contains info on the setup of a Flask application with Gunicorn as app server and nginx as webserver. The app will run with Python 3 on CentOS 7.]]></summary></entry></feed>