<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rémy Apfelbacher</title>
    <description>The latest articles on DEV Community by Rémy Apfelbacher (@reoops).</description>
    <link>https://dev.to/reoops</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F925018%2Ff4fd5b87-5a6c-4a65-a817-f1d3d1fad95a.png</url>
      <title>DEV Community: Rémy Apfelbacher</title>
      <link>https://dev.to/reoops</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/reoops"/>
    <language>en</language>
    <item>
      <title>How we setup self-hosted wiki called Outline in Zerops</title>
      <dc:creator>Rémy Apfelbacher</dc:creator>
      <pubDate>Mon, 07 Nov 2022 16:23:43 +0000</pubDate>
      <link>https://dev.to/reoops/how-we-setup-self-hosted-wiki-called-outline-in-zerops-3b07</link>
      <guid>https://dev.to/reoops/how-we-setup-self-hosted-wiki-called-outline-in-zerops-3b07</guid>
      <description>&lt;p&gt;As our code base grew there was an urgent need for a place where we could gather our knowledge about the system. There are complicated processes in &lt;a href="https://contabo.com/en/zerops/"&gt;Zerops&lt;/a&gt; and not everyone has the right information to carry them out.&lt;/p&gt;

&lt;p&gt;We already had  a wiki on Redmine, but we wanted something more modern, with good support for editing and markdown out of the box. After some research we ended up choosing &lt;a href="https://www.getoutline.com/"&gt;Outline&lt;/a&gt;, partly because it is written in a high-available way. This makes Outline a great service to run on Zerops.&lt;/p&gt;

&lt;p&gt;We used Slack for authentication purposes, so this tutorial is focused on that, but there are more &lt;a href="https://app.getoutline.com/s/770a97da-13e5-401e-9f8a-37949c19f97e/doc/authentication-7ViKRmRY5o"&gt;authentication methods&lt;/a&gt; available. First we needed to create a &lt;a href="https://app.getoutline.com/s/770a97da-13e5-401e-9f8a-37949c19f97e/doc/slack-sgMujR8J9J"&gt;Slack account&lt;/a&gt; in order to have SLACK_KEY and SLACK_SECRET values associated with our Slack account.&lt;/p&gt;

&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;p&gt;To host Outline we need following technologies.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PostgreSQL (v12+)&lt;/li&gt;
&lt;li&gt;Redis (v4+)&lt;/li&gt;
&lt;li&gt;S3 compatible object storage&lt;/li&gt;
&lt;li&gt;Node.js (v16+)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We have created a Zerops recipe for your convenience. If you want to quickly import this project, here it is.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;project&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;outline-wiki&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;wiki&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;wiki&lt;/span&gt;
    &lt;span class="na"&gt;buildFromGit&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/outline/outline&lt;/span&gt;
    &lt;span class="na"&gt;pipelineConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;wiki&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;nodejs@16&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
          &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;yarn install --frozen-lockfile&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;yarn build&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;yarn sequelize db:create --env=production-ssl-disabled || &lt;/span&gt;&lt;span class="no"&gt;true&lt;/span&gt; &lt;span class="c1"&gt;#skip creating db if already created&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;yarn sequelize db:migrate --env=production-ssl-disabled&lt;/span&gt;
          &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;.&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
          &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;node_modules&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;init&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;zcli bucket s3 create s3 outline --x-amz-acl=private&lt;/span&gt;
          &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn start&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nodejs@16&lt;/span&gt;
    &lt;span class="na"&gt;enableSubdomain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;envVariables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${s3_accessKeyId}&lt;/span&gt;
      &lt;span class="na"&gt;AWS_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;
      &lt;span class="na"&gt;AWS_S3_UPLOAD_BUCKET_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${s3_serviceId|lower}.outline&lt;/span&gt;
      &lt;span class="na"&gt;AWS_S3_UPLOAD_BUCKET_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://zerops-usc1.contabostorage.com&lt;/span&gt;
      &lt;span class="na"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${s3_secretAccessKey}&lt;/span&gt;
      &lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres://db:${db_password}@db:5432/outline&lt;/span&gt;
      &lt;span class="na"&gt;DATABASE_URL_TEST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres://db:${db_password}@db:5432/outline-test&lt;/span&gt;
      &lt;span class="na"&gt;PGSSLMODE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;disable&lt;/span&gt;
      &lt;span class="na"&gt;REDIS_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis://redis:6379&lt;/span&gt;
      &lt;span class="na"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;insert-secret-key&amp;gt;&lt;/span&gt;
      &lt;span class="na"&gt;UTILS_SECRET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;insert-util-secret-key&amp;gt;&lt;/span&gt;
      &lt;span class="na"&gt;SLACK_CLIENT_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;insert-slack-id&amp;gt;&lt;/span&gt;
      &lt;span class="na"&gt;SLACK_CLIENT_SECRET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;insert-slack-secret&amp;gt;&lt;/span&gt;
      &lt;span class="na"&gt;URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${zeropsSubdomain}&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3000&lt;/span&gt;
        &lt;span class="na"&gt;httpSupport&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;verticalAutoscaling&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;minVCpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="na"&gt;maxVCpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20&lt;/span&gt;
      &lt;span class="na"&gt;minRam&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1.25&lt;/span&gt;
      &lt;span class="na"&gt;maxRam&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;32&lt;/span&gt;
      &lt;span class="na"&gt;minDisk&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
      &lt;span class="na"&gt;maxDisk&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
    &lt;span class="na"&gt;minContainers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;maxContainers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
    &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;s3&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object-storage&lt;/span&gt;
    &lt;span class="na"&gt;objectStorageSize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
    &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keydb@6&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;NON_HA&lt;/span&gt;
    &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;hostname&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgresql@12&lt;/span&gt;
    &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HA&lt;/span&gt;
    &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This recipe creates 4 services, corresponding to 4 applications listed in the requirements section.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wiki service (Node.js)
&lt;/h3&gt;

&lt;p&gt;Everything except the Node.js service is using a default configuration, so we’re going to describe how it’s configured in this section.&lt;/p&gt;

&lt;p&gt;First we need to tell Zerops where to find the project's source code. We do that by using &lt;code&gt;buildFromGit&lt;/code&gt; key.  By default, Zerops expects &lt;code&gt;zerops.yml&lt;/code&gt; file in the root of the project directory. But we want to build a project from a repository that we don’t have write access to. To do that, we can specify a &lt;code&gt;zerops.yml&lt;/code&gt; file’s content in the &lt;code&gt;pipelineConfig&lt;/code&gt; attribute.&lt;/p&gt;

&lt;h4&gt;
  
  
  zerops.yml content in pipelineConfig
&lt;/h4&gt;

&lt;p&gt;Now we're going to describe the content of the &lt;code&gt;pipelineConfig&lt;/code&gt;. To build Outline we need &lt;code&gt;Node.js&lt;/code&gt; and &lt;code&gt;yarn&lt;/code&gt; installed. They are available in the &lt;code&gt;nodejs@16&lt;/code&gt; build image.&lt;/p&gt;

&lt;p&gt;In the build section we define necessary build steps which are copied from the &lt;a href="https://app.getoutline.com/s/770a97da-13e5-401e-9f8a-37949c19f97e/doc/from-source-BlBxrNzMIP#h-installation"&gt;Outline documentation&lt;/a&gt;. We cache the &lt;code&gt;node_modules&lt;/code&gt; folder, to make additional builds faster.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;init&lt;/code&gt; section we need to create a bucket for Outline to save assets. &lt;a href="//github.com/zeropsio/zcli"&gt;ZCli&lt;/a&gt; can be used to automate this process, by running &lt;code&gt;zcli bucket s3 create s3_hostname bucket_name&lt;/code&gt;. It creates a new &lt;code&gt;bucket&lt;/code&gt; prefixed with the s3 service id. We can find the service id in the environment variable &lt;code&gt;s3_serviceId&lt;/code&gt;. It is going to be useful in the next section when we set the environment variables.&lt;/p&gt;

&lt;h4&gt;
  
  
  Configuration using environment variables
&lt;/h4&gt;

&lt;p&gt;Outline is cloud-ready so it is pretty straight-forward to make it work with Zerops. Let’s break down the configuration of S3.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${s3_accessKeyId}&lt;/span&gt;
&lt;span class="na"&gt;AWS_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;
&lt;span class="na"&gt;AWS_S3_UPLOAD_BUCKET_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${s3_serviceId|lower}.outline&lt;/span&gt;
&lt;span class="na"&gt;AWS_S3_UPLOAD_BUCKET_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://zerops-usc1.contabostorage.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the object storage service is named &lt;code&gt;s3&lt;/code&gt; and the bucket created using &lt;code&gt;zcli&lt;/code&gt; is called &lt;code&gt;outline&lt;/code&gt;, then there’s nothing to change. Next we need to configure database connections both for the &lt;code&gt;redis&lt;/code&gt; and &lt;code&gt;postgresql&lt;/code&gt;. &lt;code&gt;PGSSLMODE&lt;/code&gt; is disabled, because the whole project is on its own private network and we do not communicate with the database over the Internet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres://db:${db_password}@db:5432/outline&lt;/span&gt;
&lt;span class="na"&gt;DATABASE_URL_TEST&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres://db:${db_password}@db:5432/outline-test&lt;/span&gt;
&lt;span class="na"&gt;PGSSLMODE&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;disable&lt;/span&gt;
&lt;span class="na"&gt;REDIS_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis://redis:6379&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For signing in purposes, Outline uses 2 secrets. They can be configured using environment variables &lt;code&gt;UTILS_SECRET&lt;/code&gt; and &lt;code&gt;SECRET_KEY&lt;/code&gt;. To generate these keys, you can use this command from the terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openssl rand &lt;span class="nt"&gt;-hex&lt;/span&gt; 32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Login and password authentication is currently not supported by Outline and so it needs to be integrated with a third party application like Slack. After creating an application as mentioned at the start of the article, you can configure Slack using the following environment variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;SLACK_CLIENT_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;insert-slack-id&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;SLACK_CLIENT_SECRET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;insert-slack-secret&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally we need to configure the &lt;code&gt;url&lt;/code&gt;, which Outline will be hosted on. If we want to use a Zerops subdomain we can leave the &lt;code&gt;URL&lt;/code&gt; variable unchanged. Otherwise, we need to configure public access in the Zerops GUI and input the configured &lt;code&gt;url&lt;/code&gt; here.&lt;/p&gt;

&lt;h4&gt;
  
  
  Configuration of ports
&lt;/h4&gt;

&lt;p&gt;On &lt;code&gt;yarn start&lt;/code&gt; Outline starts the application on port &lt;code&gt;3000&lt;/code&gt;. The application uses &lt;code&gt;http&lt;/code&gt; protocol, so we need to set &lt;code&gt;httpSupport&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Importing the project
&lt;/h3&gt;

&lt;p&gt;You can import the project in the GUI using the following button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--PmAQ1w_V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9ktn1tmg74v4x94frn9x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PmAQ1w_V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9ktn1tmg74v4x94frn9x.png" alt="Import project" width="426" height="203"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After a successful import you can access the wiki using the configured &lt;code&gt;url&lt;/code&gt; or the &lt;code&gt;zerops subdomain&lt;/code&gt;, found in the &lt;code&gt;wiki&lt;/code&gt; service.&lt;/p&gt;

&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;So that’s everything you need to run a self-hosted knowledge base on the Zerops platform. Outline is a really great piece of software and is cloud ready, so it fits nicely into the Zerops ecosystem.&lt;/p&gt;

&lt;p&gt;If you have any further questions about the setup or about Zerops in general, feel free to ask for advice in our &lt;a href="https://discord.com/invite/WDvCZ54"&gt;community discord&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ops</category>
      <category>cloud</category>
    </item>
    <item>
      <title>An introduction to the Zerops build &amp; deploy pipeline: A Golang example</title>
      <dc:creator>Rémy Apfelbacher</dc:creator>
      <pubDate>Mon, 07 Nov 2022 16:00:44 +0000</pubDate>
      <link>https://dev.to/reoops/an-introduction-to-the-zerops-build-deploy-pipeline-a-golang-example-4opd</link>
      <guid>https://dev.to/reoops/an-introduction-to-the-zerops-build-deploy-pipeline-a-golang-example-4opd</guid>
      <description>&lt;p&gt;Welcome back to our article series about &lt;strong&gt;Golang in Zerops&lt;/strong&gt;! In case you missed the previous post, have a look at &lt;a href="https://dev.to/reoops/building-a-simple-todo-app-with-gin-gonic-in-zerops-a-step-by-step-guide-5348"&gt;Building a Simple TODO App with Gin-gonic in Zerops: A step-by-step Guide&lt;/a&gt; to catch up on the basics.&lt;/p&gt;

&lt;p&gt;In this post we will focus on &lt;em&gt;building and deploying&lt;/em&gt; your own application in &lt;a href="https://contabo.com/en/zerops/"&gt;Zerops&lt;/a&gt;. All you need to do is add a &lt;a href="https://docs.contabozerops.com/documentation/build/build-config.html#introduction"&gt;&lt;code&gt;zerops.yml&lt;/code&gt;&lt;/a&gt; configuration file to the root directory of your app. We will go in detail through the file so that it works best for your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Set up your project in Zerops
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Feel free to skip this section if you are already familiar with Zerops.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Complete these steps to have Zerops ready for your application:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Login to Zerops&lt;/li&gt;
&lt;li&gt;Create a new &lt;a href="https://docs.contabozerops.com/documentation/overview/projects-and-services-structure.html#project"&gt;project&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add a new Golang &lt;a href="https://docs.contabozerops.com/documentation/overview/projects-and-services-structure.html#services-containers"&gt;service&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Note down the names of the project and the service, you will need them for future reference. For our example application we created a project called &lt;em&gt;myProject&lt;/em&gt; and a Golang service called &lt;em&gt;helloworld&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.1. Pipeline triggering (GUI/zCLI)
&lt;/h3&gt;

&lt;p&gt;Once you have your &lt;code&gt;zerops.yml&lt;/code&gt; ready, there are two options to trigger the build &amp;amp; deploy pipeline:&lt;/p&gt;

&lt;h4&gt;
  
  
  Git integration + Zerops GUI
&lt;/h4&gt;

&lt;p&gt;Recommended when your app source code is already versioned in a &lt;em&gt;git&lt;/em&gt; repository. In the detail of your Go service, go to &lt;em&gt;Build&lt;/em&gt;, &lt;em&gt;deploy&lt;/em&gt;, &lt;em&gt;run pipeline settings&lt;/em&gt;, connect with either a Github or Gitlab repository and &lt;em&gt;Activate pipeline trigger&lt;/em&gt; according to your needs.&lt;/p&gt;

&lt;h4&gt;
  
  
  zCLI
&lt;/h4&gt;

&lt;p&gt;If command line is your best friend or it just better suits your use case, you can:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Install &lt;a href="https://docs.contabozerops.com/documentation/cli/installation.html"&gt;zCLI&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Login to zCLI with an &lt;a href="https://docs.contabozerops.com/documentation/cli/authorization.html#login-using-personal-access-token"&gt;access token&lt;/a&gt; generated in GUI&lt;/li&gt;
&lt;li&gt;Run a &lt;code&gt;zcli push &amp;lt;project_name&amp;gt; &amp;lt;service_name&amp;gt;&lt;/code&gt; &lt;a href="https://docs.contabozerops.com/documentation/cli/available-commands.html#push-project-name-or-project-id-service-name-flags"&gt;command&lt;/a&gt; (in our case it is &lt;code&gt;zcli push myProject helloworld&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Nevertheless, once the pipeline is triggered, you can follow its progress in the Zerops GUI with more information provided.&lt;/p&gt;

&lt;h3&gt;
  
  
  1.2. Example application
&lt;/h3&gt;

&lt;p&gt;The most simple example was chosen to eliminate the need to test the app locally before trying out Zerops.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You are more than welcome to build, deploy and run &lt;strong&gt;your own&lt;/strong&gt; Go application in Zerops while following this example only as a guide.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Our example app is a slight modification of the famous “Hello, World!” program. There is only one file, &lt;code&gt;main.go&lt;/code&gt;. The &lt;code&gt;main&lt;/code&gt; function first sets up the &lt;code&gt;getHelloWorld&lt;/code&gt; handler function to the &lt;code&gt;/hello-world&lt;/code&gt; path, and then it starts the http server with the gin-gonic library. The &lt;code&gt;getHelloWorld&lt;/code&gt; function only prints “Hello, World!”. The server is listening on port &lt;code&gt;8080&lt;/code&gt;, which is the default &lt;a href="https://docs.contabozerops.com/documentation/routing/routing-between-project-services.html"&gt;port&lt;/a&gt; created for Go services in Zerops.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s"&gt;"fmt"&lt;/span&gt;
  &lt;span class="s"&gt;"log"&lt;/span&gt;
  &lt;span class="s"&gt;"net/http"&lt;/span&gt; &lt;span class="s"&gt;"github.com/gin-gonic/gin"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Running http server"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/hello-world"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;getHelloWorld&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":8080"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;getHelloWorld&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"received /hello-world request&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusOK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s"&gt;"Hello, World!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We also need to initialize a &lt;em&gt;go module&lt;/em&gt;, which creates &lt;code&gt;go.mod&lt;/code&gt; and &lt;code&gt;go.sum&lt;/code&gt; files. You can either do it yourself or copy the contents from our repository. The project structure is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;myProject
├── go.mod
├── go.sum
├── zerops.yml
└── main.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Build &amp;amp; deploy pipeline explained
&lt;/h2&gt;

&lt;p&gt;First we will illustrate the process and then explain the key words when editing the &lt;code&gt;zerops.yml&lt;/code&gt; file. It all starts with an image. It is either created by combining a &lt;code&gt;base&lt;/code&gt; image with &lt;code&gt;prepare&lt;/code&gt; commands, or loaded from the cache from previous builds if nothing has changed (which contains your &lt;code&gt;cached&lt;/code&gt; files as well). The image is then run inside a "build" container. After that, all &lt;code&gt;build&lt;/code&gt; commands are executed and a deployment artifact is created. It is 'deployed' to another 'runtime' container, where it is run.&lt;/p&gt;

&lt;p&gt;Now let’s have a look at the &lt;code&gt;build&lt;/code&gt; section of the &lt;code&gt;zerops.yml&lt;/code&gt; file where you can customize the process above for your Go services. Note that the service name is the root key in the &lt;code&gt;zerops.yml&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;helloworld&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# optional&lt;/span&gt;
    &lt;span class="na"&gt;prepare&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# optional&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# required&lt;/span&gt;
    &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# required&lt;/span&gt;
    &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# optional&lt;/span&gt;
&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To build your Go application, you definitely need to run the &lt;code&gt;go build ...&lt;/code&gt; command. That’s what the &lt;strong&gt;required build&lt;/strong&gt; section is for! The following command compiles our package with a single &lt;code&gt;main.go&lt;/code&gt; source file into an executable called &lt;code&gt;app&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;go build -o app main.go&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And that’s it? Well, at the moment the command would fail as we have not installed &lt;code&gt;go&lt;/code&gt; itself and set the environment. To &lt;strong&gt;save you the trouble&lt;/strong&gt; of finding the right package and going through the installation process, Zerops has created several &lt;strong&gt;base&lt;/strong&gt; images with the most common dependencies preinstalled, such as &lt;em&gt;go&lt;/em&gt; and &lt;em&gt;git&lt;/em&gt; in &lt;code&gt;go@1&lt;/code&gt; base image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;go@1&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;go build -o app main.go&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In case you need to use an older version of go or want to customize the dependencies in any way, use the &lt;a href="https://docs.contabozerops.com/documentation/build/build-config.html#prepare-optional"&gt;&lt;strong&gt;prepare&lt;/strong&gt;&lt;/a&gt; section. It is designed for any commands you might need to execute to successfully run the build commands. For instance, have you got &lt;code&gt;git&lt;/code&gt; or &lt;code&gt;wget&lt;/code&gt; in mind? No problem, here is an example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;...&lt;/span&gt;
    &lt;span class="na"&gt;prepare&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="s"&gt;-|&lt;/span&gt;
        &lt;span class="s"&gt;add-apt-repository ppa:longsleep/golang-backports&lt;/span&gt;
        &lt;span class="s"&gt;apt update&lt;/span&gt;
        &lt;span class="s"&gt;apt install -y golang-1.17 git wget&lt;/span&gt;
        &lt;span class="s"&gt;ln -s /usr/lib/go-1.17/bin/go /usr/bin/go&lt;/span&gt;
        &lt;span class="s"&gt;ln -s go-1.17 /usr/lib/go&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;go build -o app main.go&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another &lt;strong&gt;required&lt;/strong&gt; key in the &lt;em&gt;build&lt;/em&gt; section of &lt;code&gt;zerops.yml&lt;/code&gt; file is &lt;a href="https://docs.contabozerops.com/documentation/build/build-config.html#deploy"&gt;&lt;strong&gt;deploy&lt;/strong&gt;&lt;/a&gt;. You need to let Zerops know which files or folders need to be deployed to a container where your application will be running. In our case we only need the &lt;code&gt;app&lt;/code&gt; executable. The value is an array of paths allowing you to deploy single files or entire folders, see the &lt;a href="https://docs.contabozerops.com/documentation/build/build-config.html#deploy"&gt;documentation&lt;/a&gt; for all supported formats.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;go@1&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;go build -o app main.go&lt;/span&gt;
    &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last part of the build section is an optional &lt;a href="https://docs.contabozerops.com/documentation/build/build-config.html#cache-optional"&gt;&lt;strong&gt;cache&lt;/strong&gt;&lt;/a&gt;. After every build the resulting image of base image and prepare commands is cached and used next time if nothing changes in these sections of the &lt;code&gt;zerops.yml&lt;/code&gt;. However all files in your project root directory are discarded, unless you explicitly tell Zerops to cache them too. In the Go service there is not much to cache that would significantly speed up the build process compared to the &lt;code&gt;node_modules&lt;/code&gt; folder in a &lt;em&gt;Node.js&lt;/em&gt; service, for example. However let’s see how we can cache go dependencies as well - &lt;strong&gt;but take it only as an example!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It is possible to cache any files and folders in the project root directory except the &lt;code&gt;.git&lt;/code&gt; folder. Since the default folder where the go modules are stored is &lt;code&gt;~/go/pkg/mod&lt;/code&gt;, we need to change it to e.g. &lt;code&gt;$(pwd)/mod&lt;/code&gt; (&lt;em&gt;working directory is by default the root of your project&lt;/em&gt;), and then we can cache this folder. The expected format is the same as for deploy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;go@1&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;go env -w GOMODCACHE=$(pwd)/mod&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;go build -o app main.go&lt;/span&gt;
    &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;mod&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To check that the cache is applied, you should see in the logs that the dependencies are only downloaded during the first build.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--S5PPEnJu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cc0tex0i3w3t4jtrq216.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--S5PPEnJu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cc0tex0i3w3t4jtrq216.png" alt="Empty build cache" width="407" height="185"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Wot6QC6Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/97idsoxr5x1e8n6kc2be.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Wot6QC6Z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/97idsoxr5x1e8n6kc2be.png" alt="Build using cache" width="326" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we are done! The last thing is to &lt;a href="https://docs.contabozerops.com/documentation/build/build-config.html#run-part-and-its-properties"&gt;&lt;strong&gt;run&lt;/strong&gt;&lt;/a&gt; your deployed code. The only required key for Go services is &lt;code&gt;start&lt;/code&gt;, its value is expected to be a command that starts your application, such as &lt;code&gt;./app&lt;/code&gt; runs the &lt;code&gt;app&lt;/code&gt; executable in our example.&lt;/p&gt;

&lt;p&gt;The full working &lt;code&gt;zerops.yml&lt;/code&gt; file to build, deploy and run our helloworld application is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;helloworld&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;go@1&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;go build -o app main.go deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make sure the application is up and running, open the &lt;a href="https://docs.contabozerops.com/documentation/zerops-logs/runtime-logs.html"&gt;&lt;em&gt;Runtime log&lt;/em&gt;&lt;/a&gt; section in the Golang service detail.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--bq3ORtvf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/chtv97vlfsbq3q79slow.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--bq3ORtvf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/chtv97vlfsbq3q79slow.png" alt="Runtime log" width="439" height="164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have followed our example, or you have your own http server running, you can also test it by &lt;a href="https://docs.contabozerops.com/documentation/routing/using-your-domain.html"&gt;&lt;em&gt;enabling a subdomain&lt;/em&gt;&lt;/a&gt; (&lt;em&gt;for development purposes&lt;/em&gt;) or &lt;a href="https://docs.contabozerops.com/documentation/routing/using-your-domain.html"&gt;&lt;em&gt;setting up your domain&lt;/em&gt;&lt;/a&gt;. Either way, when the &lt;code&gt;/hello-world&lt;/code&gt; request is made, you should see “Hello, World!” printed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EqGh-SkC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eymgrqab4dk3huioict8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EqGh-SkC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eymgrqab4dk3huioict8.png" alt="Output in Browser" width="550" height="97"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lA6MEGbZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g0abs93l37wurnf6cx31.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lA6MEGbZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g0abs93l37wurnf6cx31.png" alt="Logging" width="477" height="211"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Easy, right? :) We are well aware that the real-life application will be more complex. If you have any issues, join our &lt;a href="https://discord.gg/SJ95zkGuS7"&gt;Discord server&lt;/a&gt; to ask any questions and/or share your solutions with others. Also check our &lt;a href="https://docs.contabozerops.com/documentation/"&gt;documentation&lt;/a&gt; to learn more about Zerops.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We are looking forward to seeing your own pipelines running!&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion (tl;dr)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;always&lt;/strong&gt; specify &lt;code&gt;build&lt;/code&gt; command&lt;/li&gt;
&lt;li&gt;use predefined &lt;code&gt;base&lt;/code&gt; images or specify custom &lt;code&gt;prepare&lt;/code&gt; commands to build your app - &lt;strong&gt;or BOTH!&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;always&lt;/strong&gt; specify which files you want to &lt;code&gt;deploy&lt;/code&gt; to a runtime container&lt;/li&gt;
&lt;li&gt;you can &lt;strong&gt;optionally&lt;/strong&gt; &lt;code&gt;cache&lt;/code&gt; any deployed files to speed up future builds&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cloud</category>
      <category>cloudnative</category>
      <category>go</category>
    </item>
    <item>
      <title>How to Autoscale Node.js Image Transformations Using Sharp and Express</title>
      <dc:creator>Rémy Apfelbacher</dc:creator>
      <pubDate>Thu, 13 Oct 2022 09:42:57 +0000</pubDate>
      <link>https://dev.to/reoops/how-to-autoscale-nodejs-image-transformations-using-sharp-and-express-27d5</link>
      <guid>https://dev.to/reoops/how-to-autoscale-nodejs-image-transformations-using-sharp-and-express-27d5</guid>
      <description>&lt;p&gt;Automation of application scaling is one of the key benefits of PaaS. The following example shows how you can use this ability in &lt;a href="https://contabo.com/en/zerops/" rel="noopener noreferrer"&gt;Zerops&lt;/a&gt;, a developer-first app platform – specifically, when transforming images by scaling or rotating them using &lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: Please be aware that the example code is not meant to have production code quality. The purpose is to demonstrate the scaling feature of &lt;a href="https://contabo.com/en/zerops/" rel="noopener noreferrer"&gt;Zerops&lt;/a&gt;. In the following, we assume that you have a &lt;a href="https://contabo.com/en/zerops/" rel="noopener noreferrer"&gt;Zerops&lt;/a&gt; account and an installed &lt;code&gt;zcli&lt;/code&gt; (Zerops command line tool) available.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing your code
&lt;/h2&gt;

&lt;p&gt;The code is fairly simple. It uses &lt;a href="https://expressjs.com/" rel="noopener noreferrer"&gt;Express&lt;/a&gt; and &lt;a href="https://sharp.pixelplumbing.com/" rel="noopener noreferrer"&gt;Sharp&lt;/a&gt; to perform the actual work. Thus please execute the following in a folder of your choice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i express sharp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will install all dependencies in a folder &lt;code&gt;node_modules&lt;/code&gt; and more importantly create files &lt;code&gt;package.json&lt;/code&gt; and &lt;code&gt;package-lock.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then please create the file &lt;code&gt;index.js&lt;/code&gt; and insert the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sharp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sharp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;expressServer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nx"&gt;expressServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/image&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requestedWidth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requestedHeight&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requestedAngle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Handling image: size to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;requestedWidth&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;x&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;requestedHeight&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; and rotation to &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;requestedAngle&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nf"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;example.jpg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestedWidth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;requestedHeight&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;sharp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inside&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;rotate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestedAngle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toFormat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toBuffer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outputBuffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;outputBuffer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;expressServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Listening on port 3000.`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use any jpg image as &lt;code&gt;example.jpg&lt;/code&gt;. Please put one in that folder.&lt;br&gt;
That's it. Just regular code (and an image).&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting up the project in Zerops
&lt;/h2&gt;

&lt;p&gt;Once you have the code ready, you need to create a project in &lt;a href="https://contabo.com/en/zerops/" rel="noopener noreferrer"&gt;Zerops&lt;/a&gt;. For that purpose, please:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;login to &lt;a href="https://contabo.com/en/zerops/" rel="noopener noreferrer"&gt;Zerops&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;create a project by clicking on &lt;code&gt;Add new project&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;specify a project name e.g. &lt;code&gt;image&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;choose &lt;code&gt;Node.js&lt;/code&gt; as service to be added&lt;/li&gt;
&lt;li&gt;click &lt;code&gt;Set to high availability?&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;click add&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This sets your Node.js runtime to high availability meaning it will perform vertical and horizontal scaling automatically. Vertical scaling means simply adjusting the amount of resources in your container whereas horizontal scaling adjusts the number of containers. You can read more about it &lt;a href="https://dev.to/reoops/scaling-your-apps-automatically-with-zerops-both-vertically-and-horizontally-36di"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Build and deploy on Zerops
&lt;/h2&gt;

&lt;p&gt;The following steps describe how you can build and deploy the example as a &lt;a href="https://contabo.com/en/zerops/" rel="noopener noreferrer"&gt;Zerops&lt;/a&gt; application.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;For &lt;a href="https://contabo.com/en/zerops/" rel="noopener noreferrer"&gt;Zerops&lt;/a&gt; to know which files to use please also create a &lt;code&gt;zerops.yml&lt;/code&gt; file next the other files:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;nodejs0&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;nodejs@16&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;npm ci&lt;/span&gt;
    &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;node_modules&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;index.js&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;example.jpg&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;node_modules&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node index.js&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Simply deploy your file
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zcli push &lt;span class="s2"&gt;"image"&lt;/span&gt; nodejs0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Enable Zerops subdomain access via &lt;code&gt;Public access &amp;amp; internal ports&lt;/code&gt; on the &lt;code&gt;nodejs0&lt;/code&gt; service and invoke the pre-generated URL in your browser.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Autoscaling in action
&lt;/h2&gt;

&lt;p&gt;So far we deployed the code only without seeing the autoscaling in action. For that purpose we will use &lt;a href="https://k6.io/" rel="noopener noreferrer"&gt;k6&lt;/a&gt; to generate some load.&lt;/p&gt;

&lt;p&gt;No worries, &lt;a href="https://contabo.com/en/zerops/" rel="noopener noreferrer"&gt;Zerops&lt;/a&gt; won't scale to infinity without keeping your expenses under control. You can adjust the maximum and minimum resources for both vertical and horizontal scaling easily every time you see fit. &lt;a href="https://contabo.com/en/zerops/" rel="noopener noreferrer"&gt;Zerops&lt;/a&gt; will do the rest.&lt;/p&gt;

&lt;p&gt;Here the script to generate some load &lt;code&gt;k6.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;k6/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;check&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;k6&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;baseUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://&amp;lt;your-generated-subdomain&amp;gt;.usc1.contabozerops.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;angle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;360&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/image?width=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;height=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;angle=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;angle&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;is status 200&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then run it by invoking&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;k6 run &lt;span class="nt"&gt;--vus&lt;/span&gt; 100 &lt;span class="nt"&gt;--duration&lt;/span&gt; 120s k6.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will simulate a 100 users requesting the image service as fast as possible during 120 seconds.&lt;/p&gt;

&lt;p&gt;Our example is mainly a CPU-intensive task so you will see the number of vCPUs (virtual CPUs) rise -- vertical scaling -- and after a short time also see more containers being created -- horizontal scaling. Please refer to the following screenshot where you see many vCPUs and 4 containers.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fptwj2se59l0pytylpyag.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fptwj2se59l0pytylpyag.png" alt="Zerops Scaling up -- vertically and horizontally"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the k6 script finished generating the load, &lt;a href="https://contabo.com/en/zerops/" rel="noopener noreferrer"&gt;Zerops&lt;/a&gt; will wait for a moment to make sure the demand has really gone down. Then you'll first see the vertical autoscaling taking action by reducing vCPUs (due to the nature of our example). The number of vCPUs has dramatically decreased.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F98somrt610p2umi9ofh6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F98somrt610p2umi9ofh6.png" alt="Zerops Scaling -- vertical cool down"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After a while, &lt;a href="https://contabo.com/en/zerops/" rel="noopener noreferrer"&gt;Zerops&lt;/a&gt; will perform the horizontal autoscaling as well. As the k6 script finished generating load and we don't have any more coming, the starting point with two containers will be reached as shown in the screenshot.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxemsx4bslozny1sax61c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxemsx4bslozny1sax61c.png" alt="Zerops Scaling -- horizontal cool down"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://contabo.com/en/zerops/" rel="noopener noreferrer"&gt;Zerops&lt;/a&gt; is performing both vertical and horizontal autoscaling out of the box with a single click. Just like shown in the example above. No need to have a special code or knowledge. It both saves money and gives you more power when required.&lt;/p&gt;

&lt;p&gt;For a deeper dive into what’s happening behind the scenes of Zerops autoscaling, check out &lt;a href="https://dev.to/reoops/scaling-your-apps-automatically-with-zerops-both-vertically-and-horizontally-36di"&gt;my previous article&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>node</category>
      <category>devops</category>
    </item>
    <item>
      <title>Building a Simple TODO App with Gin-gonic in Zerops: A step-by-step Guide</title>
      <dc:creator>Rémy Apfelbacher</dc:creator>
      <pubDate>Fri, 23 Sep 2022 07:42:45 +0000</pubDate>
      <link>https://dev.to/reoops/building-a-simple-todo-app-with-gin-gonic-in-zerops-a-step-by-step-guide-5348</link>
      <guid>https://dev.to/reoops/building-a-simple-todo-app-with-gin-gonic-in-zerops-a-step-by-step-guide-5348</guid>
      <description>&lt;p&gt;Building APIs is bread and butter for most programmers. The &lt;a href="https://contabo.com/en/zerops/" rel="noopener noreferrer"&gt;Zerops&lt;/a&gt; app platform makes this process even easier by taking care of the infrastructure.&lt;/p&gt;

&lt;p&gt;In this article, we will show you how simple it is to build a sample CRUD TODO API written in GO using gin-gonic, one of the most popular web frameworks, which saves your data to the PostgreSQL database.&lt;/p&gt;

&lt;p&gt;The resulting project is available &lt;a href="https://github.com/zeropsio/recipe-gin-postgres-api" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;First and foremost we need a database system to store and manage our data. In this case, we chose &lt;strong&gt;PostgreSQL&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Side note: If you prefer a different technology, such as &lt;strong&gt;MariaDB&lt;/strong&gt;, which Zerops also supports, the process is analogous and the source code can be found &lt;a href="https://github.com/zeropsio/recipe-gin-maria-api" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let's start by running a PostgreSQL server. We can create one very easily in Zerops, either manually or via the Zerops import function. Simply create a new project and add a PostgreSQL service (&lt;a href="https://docs.contabozerops.com/documentation/services/databases/postgresql.html#adding-the-postgresql-service-in-zerops" rel="noopener noreferrer"&gt;here’s more about PostgreSQL in Zerops&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;To test the connection or for local development, we can connect to the server with Zerops’ own command line utility called zCLI. After you have &lt;a href="https://docs.contabozerops.com/documentation/cli/installation.html#how-to-install" rel="noopener noreferrer"&gt;installed&lt;/a&gt; the zCLI, follow &lt;a href="https://docs.contabozerops.com/documentation/cli/authorization.html#login-using-personal-token" rel="noopener noreferrer"&gt;these simple steps&lt;/a&gt; to log in using a personal access token. The last step is to start a VPN connection by running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;zcli vpn start &lt;span class="o"&gt;[&lt;/span&gt;projectName]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we should be able to test that the server is accessible by running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ping6 &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;.zerops &lt;span class="c"&gt;# in our case postgresql0.zerops&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Golang
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Dependencies
&lt;/h3&gt;

&lt;p&gt;We will create the API as a GO module for easier versioning and reproducibility of builds. New GO modules are created with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;go mod init &lt;span class="o"&gt;[&lt;/span&gt;api-name]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command creates two files, &lt;code&gt;go.mod&lt;/code&gt; and &lt;code&gt;go.sum&lt;/code&gt;, which both contain dependency information.&lt;/p&gt;

&lt;p&gt;The following GO packages are used in this example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/georgysavva/scany" rel="noopener noreferrer"&gt;github.com/georgysavva/scany (v1.1.0)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/gin-contrib/cors" rel="noopener noreferrer"&gt;github.com/gin-contrib/cors (v1.4.0)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/gin-gonic/gin" rel="noopener noreferrer"&gt;github.com/gin-gonic/gin (v1.8.1)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jackc/pgx/v4" rel="noopener noreferrer"&gt;github.com/jackc/pgx/v4 (v4.17.1)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;and they can be installed using&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;go get &lt;span class="o"&gt;[&lt;/span&gt;package-url]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;More information on how to use &lt;em&gt;go modules&lt;/em&gt; can be found &lt;a href="https://go.dev/blog/using-go-modules" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Folder structure
&lt;/h3&gt;

&lt;p&gt;This being a sample application, the project structure is very simple.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;todo-api/
├── http.go
├── main.go
├── model.go
├── go.mod
├── go.sum
├── schema.sql
└── zerops.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The API source code is contained in the following files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;http.go&lt;/code&gt; - relating to the HTTP server&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;model.go&lt;/code&gt; - for communication with the DB&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;main.go&lt;/code&gt; - initialization and wiring of dependencies together&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the bootstrap of the Gin framework which is enough for it to run in Zerops. The following is our implementation of the HTTP server.&lt;/p&gt;

&lt;p&gt;First of all, we need to initialize the server in &lt;code&gt;main.go&lt;/code&gt; by calling&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To run the HTTP server smoothly, and not just in Zerops, we use several middlewares. They include CORS support, better error logging that logs to the Zerops runtime log, and a content-type header addition, which is an example of custom-written middleware.&lt;/p&gt;

&lt;p&gt;To see the logs in Zerops runtime log, we need to add the following middleware to the gin server. It handles errors and returns 500/4xx error codes depending on the error type. Additionally, it logs failed http requests with 500 to the stdout for Zerops to render on the frontend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrorLoggerT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrorTypePublic&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ErrorTypeBind&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we are done with the basic server setup, all that’s left is to register the endpoints. First, we create a router group that will consist of routes with the same path prefix.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Group&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"todos"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This API contains CRUD operations for working with the &lt;code&gt;todo&lt;/code&gt; resource. We register each &lt;code&gt;url&lt;/code&gt; path to a handler, which processes the corresponding HTTP request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getTodos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/:id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getTodo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;createTodo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;PATCH&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/:id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;editTodo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DELETE&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/:id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;handler&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deleteTodo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have chosen the &lt;code&gt;getTodos&lt;/code&gt; handler as an example in this blog post. For the full list of handlers, consult the &lt;code&gt;http.go&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="n"&gt;todoHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;getTodos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;gin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;FindAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Context&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="n"&gt;internalServerError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusOK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;todos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we can run this server on port &lt;code&gt;3000&lt;/code&gt; using the following code in the &lt;code&gt;main.go&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":3000"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running the API locally
&lt;/h2&gt;

&lt;p&gt;In the main.go file there are 3 environment variables used to connect, migrate, and seed the database. We can do this by creating an .env file with following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ZEROPS_RECIPE_DATA_SEED=["Buy milk", "Write Zerops article"]
ZEROPS_RECIPE_DROP_TABLE=1
DB_URL=postgres://${user}:${password}@${hostname}:5432/${hostname}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To get the &lt;code&gt;user&lt;/code&gt;, &lt;code&gt;password&lt;/code&gt; and &lt;code&gt;hostname&lt;/code&gt; values, see &lt;a href="https://docs.contabozerops.com/documentation/environment-variables/overview.html#referencing-environment-variables" rel="noopener noreferrer"&gt;environment variables&lt;/a&gt; in Zerops GUI.&lt;/p&gt;

&lt;p&gt;Make sure you have the zCLI VPN up and running to proceed here. &lt;br&gt;
Run this command to set the environment variables and run the API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;source&lt;/span&gt; .env &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; go run main.go http.go model.go
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running the API in Zerops
&lt;/h2&gt;

&lt;p&gt;After we completed the development of the API, and tested its functionality locally, it's time to deploy it to Zerops. To do that, we need to create a configuration file called &lt;code&gt;zerops.yml&lt;/code&gt;, which contains steps to build and deploy our app. For GO, this file is rather simple and looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;go@1&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;go build -o app main.go model.go http.go&lt;/span&gt;
    &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;app&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./app&lt;/span&gt;    
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The simplest way to deploy our API to Zerops is to integrate it as an external &lt;a href="https://docs.contabozerops.com/documentation/github/github-integration.html" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; or &lt;a href="https://docs.contabozerops.com/documentation/gitlab/gitlab-integration.html" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt; repository. We need to create a &lt;a href="https://docs.contabozerops.com/documentation/services/runtimes/golang.html#adding-the-golang-service-in-zerops" rel="noopener noreferrer"&gt;Golang&lt;/a&gt; service in Zerops. The easiest way to configure the API is to paste the environment variables in the &lt;em&gt;.env&lt;/em&gt; file format, defined in &lt;em&gt;Running the API locally&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F87ckx6xhv121bwr1fgul.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F87ckx6xhv121bwr1fgul.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the service is created, we enable a subdomain, which is a domain used for development and testing purposes. When we access it, we will see a response with todo entries from the &lt;code&gt;ZEROPS_RECIPE_DATA_SEED&lt;/code&gt; variable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe1xbnfcearnyeziyndg8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe1xbnfcearnyeziyndg8.png" alt="Image description"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Hopefully, you managed to follow this article and deploy the API to Zerops successfully. If you have any further questions, visit our &lt;a href="https://discord.com/invite/WDvCZ54" rel="noopener noreferrer"&gt;Discord channel&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>beginners</category>
      <category>go</category>
    </item>
    <item>
      <title>Scaling Your Apps Automatically with Zerops – Both Vertically and Horizontally</title>
      <dc:creator>Rémy Apfelbacher</dc:creator>
      <pubDate>Wed, 21 Sep 2022 10:42:56 +0000</pubDate>
      <link>https://dev.to/reoops/scaling-your-apps-automatically-with-zerops-both-vertically-and-horizontally-36di</link>
      <guid>https://dev.to/reoops/scaling-your-apps-automatically-with-zerops-both-vertically-and-horizontally-36di</guid>
      <description>&lt;p&gt;Auto scaling is one of the key features of PaaS systems, such as Heroku or Render. That is why we made sure to include it in &lt;a href="https://contabo.com/en/zerops/"&gt;Zerops&lt;/a&gt; and to finetune it to developers’ needs. &lt;/p&gt;

&lt;h2&gt;
  
  
  What is autoscaling?
&lt;/h2&gt;

&lt;p&gt;This feature performs an automated scaling of resources required to run your application based on its demand. So if your application isn’t used so often the auto scaling reduces the amount of required resources and thus reduces the costs. If your application is used more, then auto scaling increases the resources for the application to make sure it runs smoothly even with the higher demand.&lt;/p&gt;

&lt;p&gt;The result?&lt;/p&gt;

&lt;p&gt;On &lt;a href="https://contabo.com/en/zerops/"&gt;Zerops&lt;/a&gt;, you never pay for resources you don’t need. At the same time you don’t have to worry about traffic peaks or fast growth as the auto scaling feature handles this with no trouble.&lt;/p&gt;

&lt;h2&gt;
  
  
  Zerops automatically scales services both horizontally and vertically
&lt;/h2&gt;

&lt;p&gt;Each service you install starts with the minimum technical amount of resources: vCPU, RAM and Disk. &lt;a href="https://contabo.com/en/zerops/"&gt;Zerops&lt;/a&gt; monitors the usage of these 3 resources and if the usage exceeds a set threshold, more vCPU, RAM or Disk is allocated to the service. This is called vertical scaling. &lt;/p&gt;

&lt;p&gt;When creating a new runtime service, you can choose the minimum and maximum number of containers. If you set the maximum limit to one, the service will be run in a single container and no horizontal scaling will occur. By increasing the maximum number of containers for your service, you enable horizontal auto scaling. Zerops will then add containers depending on your application’s load. Application running on two or more containers is in High-Availability mode, which we highly recommend for production. When the load drops, containers will be gradually removed to the defined minimum. &lt;/p&gt;

&lt;p&gt;Each container and each metric (vCPU, RAM, Disk) is scaled separately. If you run your application in High-Availability mode, different containers can allocate different amounts of resources based on the current usage. There are no fixed tiers or pricing packages, you pay only for the performance and storage your application needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Auto scaling configuration for runtime services
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gUr3WC-g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5kqnityilsql9jvmafk6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gUr3WC-g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5kqnityilsql9jvmafk6.png" alt="Image description" width="880" height="569"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Both minimum and maximum limits for auto scaling are under your control. Technical minimums for vCPU, RAM and Disk are preset for each runtime service. Set the minimums and maximums for vCPU, RAM and Disk depending on the needs of your application and Zerops will scale them within defined limits. You can change the settings any time, Zerops will update the number of containers and their allocated resources accordingly. &lt;/p&gt;

&lt;p&gt;Vertical auto scaling of vCPU, RAM or Disk can be disabled independently by setting the same value for minimum and maximum. For example, you can control your RAM manually and let Zerops scale vCPU and Disk automatically. &lt;/p&gt;

&lt;p&gt;Horizontal scaling can be disabled by setting the same number for the minimum and maximum container count. Zerops then scales the service only vertically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Auto scaling configuration for databases
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--CwZym7oM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h1wv7mny358yw3m4trvd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CwZym7oM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/h1wv7mny358yw3m4trvd.png" alt="Image description" width="880" height="561"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vertical auto scaling configuration for databases and shared storage is identical to runtime services. Technical minimums for vCPU, RAM and Disk are preset according to the service type and version. &lt;/p&gt;

&lt;p&gt;You can choose 2 modes for each database and shared storage: Single Container or Highly Available mode. In the Single Container mode the database or shared storage is deployed in a single container. The number of containers cannot be increased. &lt;/p&gt;

&lt;p&gt;If the Highly Available mode is chosen, &lt;a href="https://contabo.com/en/zerops/"&gt;Zerops&lt;/a&gt; will deploy the database cluster with a fixed number of containers and control mechanisms for automatic cluster repair. Shared storage cluster contains 3 containers with all files redundantly stored on all 3 containers. The KeyDB (Redis) cluster consists of 2 containers in the master-master replica. MariaDB, PostgreSQL databases and RabbitMQ message broker are deployed in 3 containers connected in the High Availability cluster with 2 additional application balancers which ensures high reliability. You are not charged for the resources consumed by application balancers. &lt;/p&gt;

&lt;h2&gt;
  
  
  Object Storage
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--utw8qH_c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c0f7jdfkbdoztcmgnarp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--utw8qH_c--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c0f7jdfkbdoztcmgnarp.png" alt="Image description" width="546" height="336"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Amazon S3 compatible object storage is operated as a CEPH cluster and is always in the Highly Available mode. When creating a new object storage service, you set the Disk Capacity and Zerops takes care of the rest. You will be charged for the allocated disk space only. You can change the disk capacity on the go. Object storage doesn't scale automatically. &lt;/p&gt;

&lt;h2&gt;
  
  
  Zerops Auto Scaling Mechanisms
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://contabo.com/en/zerops/"&gt;Zerops&lt;/a&gt; prefers vertical scaling over horizontal scaling because vertical scaling is faster and allows finer adjustment to the required performance. Zerops checks vCPU, RAM and Disk usage in all running containers each 10 seconds. If a threshold for vertical scale up is reached, Zerops scales the container up. The minimum step for the vertical scaling is 1 vCPU, 0.25 GB RAM and 0.5 GB Disk, when the application is under a heavy load and needs to scale up fast, the steps will be automatically increased. After the application no longer needs as much power or disk space, each container is gradually scaled down to the defined minimum.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0ucwDmzW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4gvl5nojohx1cifgnilk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0ucwDmzW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4gvl5nojohx1cifgnilk.png" alt="Image description" width="880" height="863"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Zerops will add a new container (scale out your service) when a single container reaches the maximum defined limit for vertical scaling of vCPU or RAM. Zerops doesn't start a new container when the maximum disk space is reached or when the maximum container count is reached. &lt;/p&gt;

&lt;p&gt;By customizing the vertical auto scaling limits, you can cause the horizontal scaling to start earlier. For example set the vertical auto scaling maximum to 10 vCPU. Zerops will start a new container if some of the running containers are using the 10 vCPU for more than a minute.&lt;/p&gt;

&lt;p&gt;If the application no longer needs as much power, Zerops will gradually remove containers to the defined minimum count.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://contabo.com/en/zerops/"&gt;Find out more about Zerops.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>showdev</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Hello world: Zerops Edition</title>
      <dc:creator>Rémy Apfelbacher</dc:creator>
      <pubDate>Tue, 13 Sep 2022 17:19:50 +0000</pubDate>
      <link>https://dev.to/reoops/hello-world-zerops-edition-4f82</link>
      <guid>https://dev.to/reoops/hello-world-zerops-edition-4f82</guid>
      <description>&lt;p&gt;One of the most famous examples in programming is the iconic "Hello world". To follow this tradition, we use this very example to show all the steps needed for turning existing or new code into a working application powered by the new cloud application platform called &lt;a href="https://contabo.com/en/zerops/"&gt;Zerops&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  First things first: What is Zerops?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://contabo.com/en/zerops/"&gt;Zerops&lt;/a&gt; is a recently introduced PaaS such as Heroku or Render. Like them, it aims to be a universal platform that builds, deploys, runs, and manages your apps for you, be it a tiny project in development or a large one in production.&lt;/p&gt;

&lt;p&gt;With &lt;a href="https://contabo.com/en/zerops/"&gt;Zerops&lt;/a&gt;, you have a wide array of options when it comes to what you can run. You can opt for anything from a simple static web server to several runtimes for different programming languages backed up by various database management systems and other storage systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting back to our “Hello world” example
&lt;/h2&gt;

&lt;p&gt;To simplify matters we are omitting any extra steps such as connecting to GitHub and only focusing on the first steps. However, to increase the level a bit, we will take a look at two possible scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using a static web server&lt;/li&gt;
&lt;li&gt;Using a Node.js runtime&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In both cases we assume that you already have a &lt;a href="https://contabo.com/en/zerops/"&gt;Zerops&lt;/a&gt; account and an &lt;a href="https://docs.contabozerops.com/documentation/cli/installation.html"&gt;installed zCLI&lt;/a&gt; (Zerops command line tool) with proper &lt;a href="https://docs.contabozerops.com/documentation/cli/authorization.html"&gt;authentication&lt;/a&gt; set up. Please note that you could use any other runtime like for Golang or PHP.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Hello world" – the simplest way
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Create your &lt;code&gt;index.html&lt;/code&gt; file with the following content:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Hello world from Zerops!&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Login to Zerops GUI, create a new &lt;code&gt;Hello world&lt;/code&gt; project and add the &lt;code&gt;Static server&lt;/code&gt; service with the chosen hostname &lt;code&gt;staticserver0&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Then simply deploy your file using the zCLI command:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zcli deploy &lt;span class="s2"&gt;"Hello world"&lt;/span&gt; staticserver0 index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Enable Zerops subdomain access via &lt;code&gt;Public access &amp;amp; internal ports&lt;/code&gt; on the &lt;code&gt;staticserver0&lt;/code&gt; service and invoke the pre-generated URL in your browser.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Et voilà.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Hello world" – still a simple way using Node.js
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Create your &lt;code&gt;index.js&lt;/code&gt; file with the following content:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requestHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;end&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Hello world from Zerops!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requestHandler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;For &lt;a href="https://contabo.com/en/zerops/"&gt;Zerops&lt;/a&gt; to know how to start the application, please create a &lt;code&gt;zerops.yml&lt;/code&gt; configuration file next to it:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;nodejs0&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[]&lt;/span&gt;

  &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node index.js&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Login to Zerops GUI and create a new &lt;code&gt;Hello world (js)&lt;/code&gt; project and add the &lt;code&gt;Node.js&lt;/code&gt; service with the chosen hostname &lt;code&gt;nodejs0&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Then simply deploy your file using the zCLI command:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zcli deploy &lt;span class="s2"&gt;"Hello world (js)"&lt;/span&gt; nodejs0 index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Enable Zerops subdomain access via &lt;code&gt;Public access &amp;amp; internal ports&lt;/code&gt; on the &lt;code&gt;nodejs0&lt;/code&gt; service and invoke the pre-generated URL in your browser.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All done! Congrats.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summing things up
&lt;/h2&gt;

&lt;p&gt;We went through the basic steps of deploying an application to &lt;a href="https://contabo.com/en/zerops/"&gt;Zerops&lt;/a&gt;. You could see how simple it was. As you may have noticed from the sample &lt;code&gt;zerops.yml&lt;/code&gt; files, on &lt;a href="https://contabo.com/en/zerops/"&gt;Zerops&lt;/a&gt;, you can take complete control of the building, deploying, and running of your code.&lt;/p&gt;

&lt;p&gt;Coming up: more in-depth articles on how to make the most of &lt;a href="https://contabo.com/en/zerops/"&gt;Zerops&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>beginners</category>
      <category>tooling</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
