<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[AmiFactory Blog]]></title><description><![CDATA[The place where we share our knowledge, expertise and thoughts about IT and software development]]></description><link>https://web.af-site.amifactory.dev/blog/</link><image><url>https://web.af-site.amifactory.dev/blog/favicon.png</url><title>AmiFactory Blog</title><link>https://web.af-site.amifactory.dev/blog/</link></image><generator>Ghost 5.57</generator><lastBuildDate>Thu, 23 Apr 2026 16:26:06 GMT</lastBuildDate><atom:link href="https://web.af-site.amifactory.dev/blog/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Coming soon]]></title><description><![CDATA[<p>This is Af-site stage, a brand new site by Admin Adminson that&apos;s just getting started. Things will be up and running here shortly, but you can <a href="#/portal/">subscribe</a> in the meantime if you&apos;d like to stay up to date and receive emails when new content is published!</p>]]></description><link>https://web.af-site.amifactory.dev/blog/coming-soon/</link><guid isPermaLink="false">64d24d60afca710001c44ef6</guid><category><![CDATA[News]]></category><dc:creator><![CDATA[Admin Adminson]]></dc:creator><pubDate>Tue, 08 Aug 2023 14:12:48 GMT</pubDate><media:content url="https://static.ghost.org/v4.0.0/images/feature-image.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://static.ghost.org/v4.0.0/images/feature-image.jpg" alt="Coming soon"><p>This is Af-site stage, a brand new site by Admin Adminson that&apos;s just getting started. Things will be up and running here shortly, but you can <a href="#/portal/">subscribe</a> in the meantime if you&apos;d like to stay up to date and receive emails when new content is published!</p>]]></content:encoded></item><item><title><![CDATA[Devoxx Ukraine 2019]]></title><description><![CDATA[The short note on how we have attended Devoxx Ukraine 2019 - one of the biggest local tech conferences]]></description><link>https://web.af-site.amifactory.dev/blog/devoxx-ukraine-2019/</link><guid isPermaLink="false">64d24ec9afca710001c450ca</guid><category><![CDATA[devoxx]]></category><category><![CDATA[conference]]></category><dc:creator><![CDATA[Bohdan Tymoshenko]]></dc:creator><pubDate>Tue, 05 Nov 2019 13:00:00 GMT</pubDate><media:content url="https://web.af-site.amifactory.dev/blog/content/images/2019/11/1-blog-cover.jpg" medium="image"/><content:encoded><![CDATA[<img src="https://web.af-site.amifactory.dev/blog/content/images/2019/11/1-blog-cover.jpg" alt="Devoxx Ukraine 2019"><p>Here at AmiFactory, we struggle to knowledge and really enjoy technologies we use in our daily work. But always good to know if your knowledge, tech stack and architecture approaches are relevant and widely used in the industry. That&apos;s the reason we take part in specialized community events, meetups and tech conferences.</p><figure class="kg-card kg-image-card"><img src="https://web.af-site.amifactory.dev/blog/content/images/2019/11/amifactory-devoxx-2019-1-1.jpg" class="kg-image" alt="Devoxx Ukraine 2019" loading="lazy"></figure><!--kg-card-begin: markdown--><p>Last Friday-Saturday we&apos;ve attended one of the biggest tech conferences - <a href="https://devoxx.com.ua/?ref=web.af-site.amifactory.dev">Devoxx Ukraine</a>. More than 60 international speakers from the top tech companies: Edson Yanaga (<a href="https://twitter.com/yanaga?ref=web.af-site.amifactory.dev">@yanaga</a>), Burr Sutter (<a href="https://twitter.com/@burrsutter?ref=web.af-site.amifactory.dev">@burrsutter</a>), Alexey Loubyansky (<a href="https://twitter.com/@aloubyansky?ref=web.af-site.amifactory.dev">@aloubyansky</a>) from RedHat, Cliff Click (<a href="https://twitter.com/@cliff_click?ref=web.af-site.amifactory.dev">@cliff_click</a>) from CRATUS, Sebastian Daschner (<a href="https://twitter.com/@DaschnerS?ref=web.af-site.amifactory.dev">@DaschnerS</a>) from IBM, Hugh McKee (<a href="https://twitter.com/@mckeeh3?ref=web.af-site.amifactory.dev">@mckeeh3</a>) from Lightbend, Chris Thalinger (<a href="https://twitter.com/@christhalinger?ref=web.af-site.amifactory.dev">@christhalinger</a>) from Twitter, and many others.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><p><mark>The key points that we have noted during two days of talks and deep dives:</mark></p>
<ul>
<li>The world is actively moving to a <strong>microservice approach</strong> to building modern software. The technologies like Docker, Kubernetes and other infrastructure management tools help in adopting this approach. Even industry-standard enterprise technologies like Java changes to suit microservice apps requirements: project <a href="https://quarkus.io/?ref=web.af-site.amifactory.dev">Quarkus</a> from RedHat helps dramatically reduce resulting app size and memory consumption;</li>
<li><strong>Serverless</strong> and cloud functions are coming. This architecture approach helps to concentrate on business problem solving and avoid friction like infrastructure and resources management. Besides, it helps to be prepared for unpredictable loads and avoids huge bills from your cloud provider;</li>
<li>The code we write affects not only the digital world around us but also our planet <strong>ecology</strong>. Effective code and algorithms use less computing resources &#x2192; computers or servers use less electricity &#x2192; this reduces total energy-consuming, which increases every year. But in most cases, the business doesn&apos;t care about ecology and only money-driven goals work here: minimization of computation workload reduces operating expenses, minimization of traffic consumption extends your audience on the areas, where Internet access is expensive and/or limited and so on.</li>
</ul>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><p>The conference left only positive emotions despite some organization issues. Great speakers, interesting and useful talks, networking and general atmosphere. All these got us a huge portion of the inspiration and  desire do cool stuff. And who knows, maybe next time you will see our engineers in the list of speakers.</p>
<!--kg-card-end: markdown--><hr><figure class="kg-card kg-gallery-card kg-width-wide"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://web.af-site.amifactory.dev/blog/content/images/2019/11/amifactory-devoxx-2019-2-1.jpg" width="2000" height="1250" loading="lazy" alt="Devoxx Ukraine 2019" srcset="https://web.af-site.amifactory.dev/blog/content/images/size/w600/2019/11/amifactory-devoxx-2019-2-1.jpg 600w, https://web.af-site.amifactory.dev/blog/content/images/size/w1000/2019/11/amifactory-devoxx-2019-2-1.jpg 1000w, https://web.af-site.amifactory.dev/blog/content/images/size/w1600/2019/11/amifactory-devoxx-2019-2-1.jpg 1600w, https://web.af-site.amifactory.dev/blog/content/images/2019/11/amifactory-devoxx-2019-2-1.jpg 2000w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://web.af-site.amifactory.dev/blog/content/images/2019/11/amifactory-devoxx-2019-3-2.jpeg" width="2000" height="1250" loading="lazy" alt="Devoxx Ukraine 2019" srcset="https://web.af-site.amifactory.dev/blog/content/images/size/w600/2019/11/amifactory-devoxx-2019-3-2.jpeg 600w, https://web.af-site.amifactory.dev/blog/content/images/size/w1000/2019/11/amifactory-devoxx-2019-3-2.jpeg 1000w, https://web.af-site.amifactory.dev/blog/content/images/size/w1600/2019/11/amifactory-devoxx-2019-3-2.jpeg 1600w, https://web.af-site.amifactory.dev/blog/content/images/2019/11/amifactory-devoxx-2019-3-2.jpeg 2000w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://web.af-site.amifactory.dev/blog/content/images/2019/11/amifactory-devoxx-2019-4-2.jpg" width="2000" height="1250" loading="lazy" alt="Devoxx Ukraine 2019" srcset="https://web.af-site.amifactory.dev/blog/content/images/size/w600/2019/11/amifactory-devoxx-2019-4-2.jpg 600w, https://web.af-site.amifactory.dev/blog/content/images/size/w1000/2019/11/amifactory-devoxx-2019-4-2.jpg 1000w, https://web.af-site.amifactory.dev/blog/content/images/size/w1600/2019/11/amifactory-devoxx-2019-4-2.jpg 1600w, https://web.af-site.amifactory.dev/blog/content/images/2019/11/amifactory-devoxx-2019-4-2.jpg 2000w" sizes="(min-width: 720px) 720px"></div></div></div></figure>]]></content:encoded></item><item><title><![CDATA[AmiFactory Named Top Developer In Ukraine By Clutch.co: Thank You!]]></title><description><![CDATA[We are proud to be a top developer on the list this year! Many thanks to our partners  - it's our common achievement!]]></description><link>https://web.af-site.amifactory.dev/blog/top-developer-in-ukraine-by-clutch-co-2019/</link><guid isPermaLink="false">64d24ec9afca710001c450c9</guid><category><![CDATA[clutch.co]]></category><dc:creator><![CDATA[Bohdan Tymoshenko]]></dc:creator><pubDate>Thu, 31 Oct 2019 15:00:00 GMT</pubDate><media:content url="https://web.af-site.amifactory.dev/blog/content/images/2019/10/0-blog-cover.jpg" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://web.af-site.amifactory.dev/blog/content/images/2019/10/0-blog-cover.jpg" alt="AmiFactory Named Top Developer In Ukraine By Clutch.co: Thank You!"><p>AmiFactory Team is proud to partner with clients that need development for custom software, web, and mobile applications. Since 2017, we&#x2019;ve been dedicated to helping business grow and evolve in the <a href="https://www.forbes.com/sites/adrianbridgwater/2019/10/01/why-software-customization-is-a-good-thing--and-a-bad-thing/?ref=web.af-site.amifactory.dev#967e44675865">modern industry</a>.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><p>Clutch, the B2B ratings and reviews platform, recently published their <a href="https://clutch.co/ua/app-developers/kiev?page=3&amp;ref=web.af-site.amifactory.dev">Clutch list for Kiev</a> developers that stand out amongst the influx of development companies in Ukraine. AmiFactory is proud to be a top developer on the list this year and we owe it all to our partners.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><blockquote>
<p><em>It&apos;s hard to be a newcomer on the market, especially if you are small. We are proud of what we do and happy to see our partners growing together with our team. We are happy to see that people love our services and quality we provide. We are very grateful to our partners for their trust and thank you Clutch.co for your support!&#x201D;</em> &#x2013; <strong>Volodymyr Loboda, CEO at AmiFactory Team</strong></p>
</blockquote>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><p>We&#x2019;re looking forward to continuing our partnerships with our current clients are with the exposure from this award, we can&#x2019;t wait to meet our new partners!</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><p>If you&#x2019;re interested in getting a project kick-started with our developers at AmiFactory, send us a message for your <a href="https://amifactory.team/contacts.html?ref=web.af-site.amifactory.dev">project consultation</a>!</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[How to deploy a website?]]></title><description><![CDATA[Today we are going to talk about website/frontend deployment, starting from the simplest one to the fully automatic solution.]]></description><link>https://web.af-site.amifactory.dev/blog/how-to-deploy-a-website/</link><guid isPermaLink="false">64d24ec9afca710001c450c8</guid><category><![CDATA[website]]></category><category><![CDATA[frontend]]></category><category><![CDATA[automation]]></category><category><![CDATA[CI/CD]]></category><category><![CDATA[GitLab]]></category><dc:creator><![CDATA[Bohdan Tymoshenko]]></dc:creator><pubDate>Tue, 14 May 2019 07:00:00 GMT</pubDate><media:content url="https://web.af-site.amifactory.dev/blog/content/images/2019/05/0-blog-cover-3.png" medium="image"/><content:encoded><![CDATA[<!--kg-card-begin: markdown--><img src="https://web.af-site.amifactory.dev/blog/content/images/2019/05/0-blog-cover-3.png" alt="How to deploy a website?"><p>Today we are going to talk about the ways of deploying websites. There are a lot of methods, approaches and practice on the Internet to deal with this task. But what the difference between them? What pros and cons each has? What is the best suitable for your case? I&apos;m going to answer these questions based on my personal experience and give you a base knowledge on this topic. I think this will be a good start to get to know DevOps practices as well.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h2 id="content">Content</h2>
<ol>
<li><a href="#motivation">Motivation</a></li>
<li><a href="#prerequirements">Pre-requirements: domain, remote server/VPS, ssh/ftp access</a></li>
<li><a href="#simplewayscprsyncftp">Simple way: scp, rsync, FTP</a></li>
<li><a href="#versioncontrolanddeployusinggit">Adding version control and deploy using git</a></li>
<li><a href="#automationwithgitlabcicd">Automatization: GitLab CI/CD;</a></li>
<li><a href="#summary">Summary</a></li>
</ol>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h2 id="motivation">Motivation</h2>
<p>Quite often my friends/colleagues/familiars write me something like that:<br>
&#x2014; <em>Hi bro, I&apos;ve made a small website/simple web app for myself/my friend/coffee shop/{{ reason-to-launch-website }}. Everything works fine, but how to deploy it? Can you help me?</em><br>
&#x2014; I see, but you can google how to make this. There are a lot of tutorials on the Internet.</p>
<p>And in a few hours/days/weeks:<br>
&#x2014; <em>Listen, I&apos;ve found a tutorial and made the same steps, but something went wrong/doesn&apos;t work/I&apos;m stuck. Could you help, please?</em><br>
&#x2014; Ok, let&apos;s see...</p>
<p>The time is a valuable resource and to stop spending it on this topic again and again, I decided to write this post about website deployment. This is not a simple step-by-step tutorial, it&apos;s short guide about the methods of delivering and deployment websites to a remote server. <mark>This guide is also useful for simple web-apps or SPA (Single Page Applications).</mark> The only remark: you may need to update Nginx configuration to make it run correct.</p>
<p>So, when next time someone asks me to help with deploy - I&apos;ll just share the link to this post.</p>
<p>I know that this guide isn&apos;t perfect and if you still can&apos;t organize deployment or have questions/suggestions/recommendations - feel free to write a comment. This might help improve this guide and make it as useful as possible.</p>
<p>Btw, this post is the first post in AmiFactory Team blog. Our motivation to start the company blog is going from one of our company principles: Creating and launching products, together with sharing knowledge with the developer&apos;s community. This blog is our contribution to increasing the level of competence and awareness of web developers.</p>
<p>I suggest highlighting the available methods of website deployment, starting from the simplest one to the fully automatic solution, similar to what we use at <a href="https://amifactory.team/?ref=web.af-site.amifactory.dev">AmiFactory</a>.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h2 id="prerequirements">Pre-requirements</h2>
<p>For success website/frontend deploy you need a few key components:</p>
<ol>
<li>Your own domain name;</li>
<li>VPS or any other Linux host, that is accessible from the Internet and you have access to it;</li>
<li>The website/frontend project itself, that you have run and tested locally on your laptop.</li>
</ol>
<p>This guide doesn&#x2019;t contain steps to get a domain name or VPS but let&#x2019;s say a few words about each.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h3 id="domainname">Domain name</h3>
<p>There are a lot of domain registrants today. Here is a <a href="https://www.techradar.com/news/best-domain-name-registrar?ref=web.af-site.amifactory.dev">TOP of domain registrants</a> from TechRadar. I personally prefer <a href="https://www.namecheap.com/?ref=web.af-site.amifactory.dev">Namecheap</a> and <a href="https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/domain-register.html?ref=web.af-site.amifactory.dev">Amazon Route53</a>. The first provides good pricing and the second one provides good API for DNS records management.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h3 id="hosting">Hosting</h3>
<p>For this guide, we will use VPS (Virtual Private Server). It&apos;s a good compromise between shared hosting and dedicated server. You get more control over the host, more stable computation resources and don&apos;t pay a lot as with dedicated server. Here is a <a href="https://www.techradar.com/news/cheap-vps-hosting-deals?ref=web.af-site.amifactory.dev">TOP of VPS providers</a> from TechRadar. I personally recommend <a href="https://digitalocean.com/?ref=web.af-site.amifactory.dev">DigitalOcean</a> and here is why:</p>
<ol>
<li>Simple and understandable admin panel and good documentation;</li>
<li>Good pricing combined with good VPS configurations;</li>
<li>Several data centers to place you VPS closer to your target audience;</li>
<li>Powerful API for automatization and useful integrations.</li>
</ol>
<p>If you still don&#x2019;t have hosting, consider using <a href="https://m.do.co/c/aaf61047778f?ref=web.af-site.amifactory.dev"><strong>our referral link</strong></a> to Sign-up DigitalOcean account. You will get $100 credit over 60 days, which is enough to try all described methods. Please notice that this is an option, so feel free to choose any VPS provider you like - this guide does not depend on DO VPS.</p>
<p>Independent to selected hosting provider <mark>you will need VPS with at least 1vCPU, 1GB RAM, 20GB storage, Ubuntu 18.04, public IP and access via ssh</mark>. Initial setup for DO image of Ubuntu 18.04 you can find <a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-18-04?ref=web.af-site.amifactory.dev">here</a> or you can use Ansible Playbook from demo project repository, but let&#x2019;s talk about this later. Also, please notice that <mark>your domain should point to your VPS IP address</mark>, so <a href="https://en.wikipedia.org/wiki/List_of_DNS_record_types?ref=web.af-site.amifactory.dev">DNS A record</a> should exist. Also, here is a good guide on <a href="https://www.digitalocean.com/community/tutorials/ssh-essentials-working-with-ssh-servers-clients-and-keys?ref=web.af-site.amifactory.dev">SSH essentilas and setup</a>.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h3 id="website">Website</h3>
<p>For this guide, we will use a prepared <a href="https://gitlab.com/AmiFactoryTeam/my-awesome-website?ref=web.af-site.amifactory.dev">demo website project</a>. Yes, it hosts on GitLab. We will discuss why we choose GitLab over Github or BitBucket. This is a simple static website with additional pre-processing to bring it closer to a real project. <a href="https://gitlab.com/AmiFactoryTeam/my-awesome-website/tree/master/misc?ref=web.af-site.amifactory.dev">&apos;misc&apos;</a> directory contains bash script <code>sync-with-ftp.sh</code> for FTP sync and <code>post-receive</code> git-hook, that you will need during &apos;deploy using git&apos; method. <a href="https://gitlab.com/AmiFactoryTeam/my-awesome-website/tree/master/src?ref=web.af-site.amifactory.dev">&apos;src&apos;</a> directory contains the sources of the demo website.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h3 id="additionals">Additionals</h3>
<p>A few words about <a href="https://gitlab.com/AmiFactoryTeam/my-awesome-website?ref=web.af-site.amifactory.dev">git repository</a> for this guide. It contains several directories. <a href="https://gitlab.com/AmiFactoryTeam/my-awesome-website/tree/master/infra?ref=web.af-site.amifactory.dev">&apos;infra&apos;</a> directory contains <a href="https://www.ansible.com/?ref=web.af-site.amifactory.dev">Ansible</a> playbooks for initial Ubuntu 18.04 setup, Nginx web server, acquiring TLS certificates from <a href="https://letsencrypt.org/?ref=web.af-site.amifactory.dev">Let&apos;sEncrypt</a> using http-challenge to enable TLS support (HTTPS) for the website. Ansible should be installed locally and ssh access to the remote host required. If you don&#x2019;t have Ansible installed, you can use the <a href="https://hub.docker.com/r/ansible/container-conductor-alpine-3.5?ref=web.af-site.amifactory.dev">official Ansible Docker image</a> to run it docker container. Or you can use <a href="https://gitlab.com/AmiFactoryTeam/docker-ansible?ref=web.af-site.amifactory.dev">our Ansible Docker image</a>, based on Ubuntu 16.04 with Ansible installed. In both cased <a href="https://www.docker.com/products/container-runtime?ref=web.af-site.amifactory.dev">Docker</a> install required.</p>
<p>You don&apos;t have to be Docker or Ansible guru - only basic knowledge and minimal experience required to proceed steps, where these technologies involved.</p>
<p>Anyway, running Ansible playbook locally, you can do the initial setup of Ubuntu 18.04:</p>
<pre><code>$ cd infra
$ ansible-playbook -i inventory/webserver -e &apos;ansible_ssh_user=root&apos; -v playbook-initial.yml
</code></pre>
<p>Then you can run Nginx, Certbot (Let&apos;sEncrypt) and other stuff installation:</p>
<pre><code>$ ansible-playbook -i inventory/webserver -v playbook.yml
</code></pre>
<p>As result, you should have fully prepared web server, ready to website or frontend hosting. One short notice: we&apos;ve tested these Ansible playbooks with Digital Ocean Ubunty 18.04 droplet, so if you face unexpected issues - let me know in comments.</p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h2 id="simplewayscprsyncftp">Simple way: scp, rsync, FTP</h2>
<p>Let&#x2019;s talk a little bit about the essentials of any website deployment process. In basics, if we omit all pre-processing, linters, minification and others, it&apos;s just moving files from developers laptop to the web server host. From this perspective, <mark>the simplest way to deploy a website is a direct local files copy to a remote host.</mark></p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h3 id="scp">SCP</h3>
<p><img src="https://web.af-site.amifactory.dev/blog/content/images/2019/05/Blog-illustration-1.png" alt="How to deploy a website?" loading="lazy"><br>
Utility <a href="https://linux.die.net/man/1/scp?ref=web.af-site.amifactory.dev"><strong>scp</strong></a> (stands for &apos;secure copy&apos;) allow copying files from a local host to a remote host using ssh protocol. According to this, you should have working ssh connection to a remote host. <code>scp</code> allows copying a single file or whole directory:</p>
<pre><code>$ scp -C dist/index.html ubuntu@webserver:/opt/webserver/www/example.com/
index.html                                   100%  670    22.1KB/s   00:00 

$ scp -rC dist/* ubuntu@webserver:/opt/webserver/www/example.com/
404.html                                     100%  660    21.3KB/s   00:00
main.min.css                                 100%  770    25.6KB/s   00:00 
main.css                                     100% 1337    44.5KB/s   00:00 
keyboard.jpg                                 100%  240KB   1.3MB/s   00:00 
index.html                                   100%  670    21.7KB/s   00:00 
main.min.js                                  100%  125     4.2KB/s   00:00 
main.js                                      100%  168     5.5KB/s   00:00 
</code></pre>
<p>where:<br>
<code>-r</code> - recursive copying;<br>
<code>-C</code> - use compression.</p>
<p>More details about <code>scp</code> params and using you can get on <code>man scp</code></p>
<h4 id="pros">Pros:</h4>
<ul>
<li>Simple using;</li>
<li>Support compression;</li>
<li>Secure because of data is transmitting to a remote host using ssh protocol.</li>
</ul>
<h4 id="cons">Cons:</h4>
<ul>
<li><code>scp</code> uses a <a href="https://stackoverflow.com/questions/20244585/how-does-scp-differ-from-rsync?ref=web.af-site.amifactory.dev">plain linear copy</a>. This means that even if the file didn&apos;t change locally, it will be transmitted over the network to a remote host anyway;</li>
<li><code>scp</code> works slowly in case you need to copy a large number of files;</li>
<li><code>scp</code> doesn&apos;t have a lot of copy options.</li>
</ul>
<p><code>scp</code> was developed to copy files and like any good Linux utility, it deals greatly with its single task. In our case during website development, only the part of files change frequently. That&apos;s why we can clarify previous definition: <mark>initial website&apos;s files copy with future synchronization of changes.</mark></p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h3 id="rsync">RSYNC</h3>
<p><a href="https://linux.die.net/man/1/rsync?ref=web.af-site.amifactory.dev"><strong>rsync</strong></a> utility offers described functionality. The main difference between <code>scp</code> and <code>rsync</code> is the implementation of a special delta transfer algorithm and a few optimizations to make the operation faster.</p>
<p><img src="https://web.af-site.amifactory.dev/blog/content/images/2019/05/Blog-illustration-2.png" alt="How to deploy a website?" loading="lazy"></p>
<p>During the first deploy, <code>rsync</code> works in the same way as <code>scp</code>, but during the next deploy, it will copy changed files only. This greatly reduces deploy time:</p>
<pre><code>$ rsync -rvzhe ssh src/* ubuntu@webserver:/opt/webserver/www/web.afstaging.network/
building file list ... done
404.html
index.html
css/
css/main.css
css/main.min.css
img/
img/keyboard.jpg
js/
js/main.js
js/main.min.js

sent 246.90K bytes  received 192 bytes  98.84K bytes/sec
total size is 249.01K  speedup is 1.01
</code></pre>
<p><code>-r</code> - recurse into directories;<br>
<code>-v</code> - increase verbosity;<br>
<code>-z</code> - compress file data during the transfer;<br>
<code>-h</code> - output numbers in a human-readable format;<br>
<code>-e ssh</code> - specify the remote shell to use, ssh in this case.</p>
<p>You can get more details <code>rsync</code> and it&apos;s options on <code>man rsync</code></p>
<h4 id="pros">Pros:</h4>
<ul>
<li>Synchronization instead of full linear copy;</li>
<li>Several synchronization options: selecting files comparison method, removing remote files that were removed locally, etc;</li>
<li>Secure because of data might be transmitted to a remote host using ssh protocol.</li>
</ul>
<h4 id="cons">Cons:</h4>
<ul>
<li>A little more complex because of a large number of params to run.</li>
</ul>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h3 id="ftp">FTP</h3>
<p><img src="https://web.af-site.amifactory.dev/blog/content/images/2019/05/Blog-illustration-3.png" alt="How to deploy a website?" loading="lazy"><br>
Both described methods are effective and might be used during the website development process. But they both suppose you have ssh access to a remote host, which is not always possible, especially if you choose shared hosting without ssh access or your workstation works under Windows. Yes, I know and even used tools like PowerShell, Cygwin, MinGW, PuTTY and other stuff. But in most cases using <strong>FTP</strong> rid you of setting up a local environment and ssh access to a remote host. Besides, there are many good, multiplatform and mature ftp clients with GUI. The most popular is <a href="https://filezilla-project.org/?ref=web.af-site.amifactory.dev">FileZilla</a>. Similar to <code>scp</code>, FTP allows copying of  a single file or a whole directory. As with <code>rsync</code>, you can sync files by date or/and file size. I won&apos;t describe work with FileZilla, because of:</p>
<ul>
<li>It&apos;s a fairly wide topic;</li>
<li>FileZilla GUI is quite intuitive even for beginners.</li>
</ul>
<p>Instead of this, I will show how to sync files via FTP using CLI utility <a href="https://linux.die.net/man/1/lftp?ref=web.af-site.amifactory.dev"><strong>lftp</strong></a>. Let&apos;s create a simple Bash script named <code>ftp-sync.sh</code>, which we will call each time we need to deploy local changes:</p>
<pre><code>#!/bin/bash

# This is a simple bash script, which call lftp utility to sync 
# your local website directory (LCD) with remote host website directory(RCD)

# FTP username (replace with your ftp-user login)
USER=ftp-user
# FTP user password (replace with your ftp-user password)
PASS=my-secret-password
# Website address (replace with your website domain)
HOST=&quot;example.com&quot;
# Website local source directory (dist) (replace with your project path)
LCD=&quot;/&lt;path to your project on your laptop&gt;/dist&quot;
# FTP target directory (related to FTP root) (usually the same as domain name, exampe.com)
RCD=&quot;example.com&quot;

lftp -f &quot;
open $HOST
user $USER $PASS
lcd $LCD
mirror --continue --reverse --delete --verbose $LCD $RCD
bye
&quot; 
</code></pre>
<p>Add executable rights for the user and run it:</p>
<pre><code>$ chmod u+x ftp-sync.sh
$ ./ftp-sync.sh
source: Is a directory
Transferring file &apos;404.html&apos;
Transferring file &apos;index.html&apos;
Making directory &apos;css&apos;
Transferring file &apos;css/main.css&apos;
Transferring file &apos;css/main.min.css&apos;
Making directory &apos;img&apos;
Transferring file &apos;img/keyboard.jpg&apos;

Making directory &apos;js&apos;
Transferring file &apos;js/main.js&apos;
Transferring file &apos;js/main.min.js&apos;
</code></pre>
<p>But you should keep in mind than FTP is a more complex protocol/tooling comparing to ssh/scp/rsync. FTP establishes a separate TCP connection for command stream and separate TCP connection for directory listing and files transfer. Also, there are two work modes of FTP server. They affect the way transfer connections establishes:</p>
<ul>
<li>Active mode;</li>
<li>Passive mode.</li>
</ul>
<p>In Active mode, FTP client starts listening TCP connections on selected port and send this port number to FTP server. FTP server establishes TCP transfer connection with the client on a received port. Work in Active mode is impossible if a client is behind Firewall, NAT or for any other reason can&apos;t receive TCP connections on the selected port.</p>
<p>To solve this problem, FTP server might work in Passive mode. The client asks the server to work in Passive mode. If the server provides this feature, the client receives IP address and the port number on which the server waits for TCP transfer connections. In Passive mode, FTP server has dedicated port range and selects free port for each new client. This port range should be opened on remote host firewall.</p>
<p>Also, FTP might use TLS encryption to avoid clear text files transmission.</p>
<h4 id="pros">Pros:</h4>
<ul>
<li>Extremely simple and widely used method to deploy a static website;</li>
<li>Might be used even if ssh access to a remote host is not provided;</li>
<li>Many multiplatform FTP clients with GUI or CLI;</li>
<li>Secure in case of using explicit FTPS (FTP + TLS).</li>
</ul>
<h4 id="cons">Cons:</h4>
<ul>
<li>It&apos;s quite often lack of TLS support on FTP server side;</li>
<li>It&apos;s a little hard to set up the FTP server in a case when hosting provider doesn&apos;t provide FTP access;</li>
<li>Additional software (FTP server) installed on a remote host;</li>
<li>Passive FTP mode requires a range of opened ports on a remote host Firewall.</li>
</ul>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h2 id="versioncontrolanddeployusinggit">Version control and deploy using git</h2>
<p>Today it&apos;s hard to image the working process on any software project without using Version Control System (VCS). If you still don&apos;t use <a href="https://git-scm.com/?ref=web.af-site.amifactory.dev"><strong>Git</strong></a> - you definitely should start, even if you don&apos;t plan to use GitHub/GitLab.</p>
<p><img src="https://web.af-site.amifactory.dev/blog/content/images/2019/05/Blog-illustration-4.png" alt="How to deploy a website?" loading="lazy"></p>
<p>In addition to the main purpose, it&apos;s convenient to use <code>git</code> as a tool for deploying. This method is based on using <a href="https://githooks.com/?ref=web.af-site.amifactory.dev">git-hooks</a>. <mark>The basic idea is to have <a href="https://mijingo.com/blog/what-is-a-bare-git-repository?ref=web.af-site.amifactory.dev">a bare git repository</a> on the remote host (your web server). You push your local changes to this remote repository. After every success push, the remote git repository will run post-receive hook, which is a plain Bash script. This script will run the deployment process</mark></p>
<p>Pre-requirements:</p>
<ul>
<li><code>git</code> should be installed locally on the workstation and on the remote host;</li>
<li>You should have working ssh access to the remote host.</li>
</ul>
<p>First, let&apos;s set up the <strong>remote host</strong>. Log in to the remote host and create a bare git repository:</p>
<pre><code>$ ssh user@remote-host
$ sudo mkdir /opt/webserver/www/example.com
$ sudo chown ubuntu:ubuntu /opt/webserver/www/example.com
$ git init --bare ~/myawesomewebsite.git
Initialized empty Git repository in /home/ubuntu/myawesomewebsite.git

$ cd ~/myawesomewebsite.git
</code></pre>
<p>Add git post-receive hook:</p>
<pre><code>$ touch hooks/post-receive
$ nano hooks/post-receive
</code></pre>
<pre><code>#!/bin/bash

# This is a simple Git post-receive hook script, which:
# 1. Runs when new revisions (commits) arrives
# 2. Checkouts working copy to TMP directory
# 3. Backups current website root directory by renaming it adding &apos;_bck&apos; ending
# 4. Moving a new working copy from TMP directory to website root directory TARGET
# 5. Removes backup at the end

# Directory, where website content will be unzipped (temporary)
TMP=&quot;/tmp/example.com/&quot;

# Root Directory, where website content located
TARGET=&quot;/opt/webserver/www/example.com&quot;

# Root Directory of your bare Git repository
GIT_DIR=&quot;/home/ubuntu/my-awesome-site.git&quot;
BRANCH=&quot;master&quot;

while read oldrev newrev ref
do
	# only checking out the master (or whatever branch you would like to deploy)
	if [ &quot;$ref&quot; = &quot;refs/heads/$BRANCH&quot; ];
	then
		echo &quot;Ref $ref received. Deploying ${BRANCH} branch to production...&quot;
		rm -rf $TMP
		mkdir -p $TMP
		git --work-tree=$TMP --git-dir=$GIT_DIR checkout -f $BRANCH
		mv $TARGET &quot;${TARGET}_bck&quot;
		mv &quot;${TMP}/src&quot; $TARGET
		rm -rf &quot;${TARGET}_bck&quot;
		echo &quot;Ref $ref received. Deploy finished.&quot;
	else
		echo &quot;Ref $ref received. Doing nothing: only the ${BRANCH} branch may be deployed on this server.&quot;
	fi
done
</code></pre>
<pre><code>$ chmod +x ~/myawesomewebsite.git/hooks/post-receive
</code></pre>
<p>Now the remote host is ready. Let&apos;s move to the <strong>workstation</strong> set up. Your project/website should be under <code>git</code> control. If not, you should:<br>
Initialize git repository in your project root directory:</p>
<pre><code>$ cd your-project-dir
$ git init .
Initialized empty Git repository in /home/username/your-project-dir/.git
</code></pre>
<p>Add project files to git stage. Don&apos;t forget about .gitignore:</p>
<pre><code>$ git add .
$ git -m &quot;Initial commit&quot;
[master (root-commit) dd974a7] Initial commit
 33 files changed, 10582 insertions(+)
 create mode 100644 .dockerignore
 create mode 100644 .gitattributes
 create mode 100644 .gitignore
 create mode 100644 .gitlab-ci.yml
 create mode 100644 LICENSE
 create mode 100644 Readme.md
 create mode 100755 gulpfile.js
 create mode 100644 infra/environments/default.yml
 create mode 100644 infra/environments/webserver.yml
 create mode 100644 infra/files/91-custom
 create mode 100644 infra/files/default.nginx.conf
 create mode 100644 infra/files/ftponly
 create mode 100644 infra/files/nginx-restart.sh
 create mode 100644 infra/files/ssl-params.conf
 create mode 100644 infra/inventory/webserver
 create mode 100644 infra/playbook-initial.yml
 create mode 100644 infra/playbook.yml
 create mode 100644 infra/roles/vsftpd/tasks/main.yml
 create mode 100644 infra/templates/nginx.conf.j2
 create mode 100644 infra/templates/ssl.conf.j2
 create mode 100644 infra/templates/vsftpd.conf.j2
 create mode 100644 misc/deploy/deploy.sh
 create mode 100755 misc/ftp/sync-with-ftp.sh
 create mode 100755 misc/git/post-receive
 create mode 100644 package-lock.json
 create mode 100755 package.json
 create mode 100644 src/copy/img/keyboard.jpg
 create mode 100644 src/js/main.js
 create mode 100644 src/sass/main.scss
 create mode 100644 src/templates/404.twig
 create mode 100644 src/templates/components/head-content.twig
 create mode 100644 src/templates/index.twig
 create mode 100644 src/templates/layouts/default.twig
</code></pre>
<p>Add a new remote git repository:</p>
<pre><code>$ git remote add webserver ubuntu@webserver:myawesomewebsite.git
</code></pre>
<p>Now we are ready to push our working copy to the remote host. During the push, you will see post-receive hook output in the console:</p>
<pre><code>$ git push webserver master
Enumerating objects: 55, done.
Counting objects: 100% (55/55), done.
Delta compression using up to 8 threads.
Compressing objects: 100% (38/38), done.
Writing objects: 100% (55/55), 318.12 KiB | 11.78 MiB/s, done.
Total 55 (delta 0), reused 0 (delta 0)
remote: Ref refs/heads/master received. Deploying master branch to production...
remote: Already on &apos;master&apos;
remote: Ref refs/heads/master received. Deploy finished.
To webserver:myawesomewebsite.git
   07e0709..b7df4f4  master -&gt; master
</code></pre>
<h4 id="pros">Pros:</h4>
<ul>
<li>A simple and understandable deploy process - <code>git push</code>;</li>
<li>Easy to integrate/call from your favorite IDE/TextEditor, that has minimal <code>git</code> integration;</li>
<li>Because of git-hook is a plain Bash script, you can do any customization of your deploy process: copy files to the required directory, do a backup, restart a web server, etc.;</li>
<li>Ability to quickly revert deploy to previous state using <a href="https://git-scm.com/docs/git-log?ref=web.af-site.amifactory.dev">git log</a> and <a href="https://git-scm.com/docs/git-revert?ref=web.af-site.amifactory.dev">git revert</a>;</li>
<li>No need to use any additional tools for deploy in case you use <code>git</code> as VCS;</li>
<li>Effective and fast, because <code>git push</code> transmit the only diff between your local and remote repositories;</li>
<li>Secure, because of data between two git repositories transmits using ssh protocol.</li>
</ul>
<h4 id="cons">Cons:</h4>
<ul>
<li>Git post-receive hook calls only ones after all git refs will be updated. So, in a case of unsuccessful deploy, you can&apos;t call hook again. The only way is to push another commit;</li>
<li>You need to install and configure <code>git</code> and git-hook on the remote host;</li>
<li>Errors in git-hook might lead to website downtime.</li>
</ul>
<p>Deploy using <code>git</code> is still widespread and applies on many small and medium projects.<br>
<code>git</code> saves the history of code-base changes, but neither save the history of deployments nor deploy logs.<br>
Also, if you use &apos;central&apos; git repository on GitHub/GitLab/{{ your-option }} services, you need always remember to push both to &apos;central&apos; and &apos;deploy&apos; repository. If several developers work on the same project, we got an additional problem of synchronization of &apos;central&apos; and &apos;deploy&apos; repository.</p>
<p>And the last: Deploying in this way a little restricts or complicates the possible additional steps that need to be made during the deploy process. I&apos;m talking about actions, that you do manually to prepare your project to deployment: preprocessing, minification, linters, etc. Several problems here:</p>
<ul>
<li>Preparation steps execute on the remote host, that hosts website. This might affect its performance;</li>
<li>Preparation steps usually require installing additional tools on the host;</li>
<li>In case of unsuccessful deploy, you will have to investigate the reason on the remote host.</li>
</ul>
<p><mark>To rid of described problems we can move deployment process on a separate server. Here we come to CI/CD topic.</mark></p>
<!--kg-card-end: markdown--><p></p><!--kg-card-begin: markdown--><h2 id="automationwithgitlabcicd">Automation with GitLab CI/CD</h2>
<p>So, we&#x2019;ve defined several new requirements during applying the previous method:</p>
<ul>
<li>Codebase synchronization;</li>
<li>Need to execute additional actions during preparing to deployment;</li>
<li>Deployment history and logs;</li>
<li>Move deployment process execution to separate host.</li>
</ul>
<p>All these tasks (plus some not listed here) can be done by using a good <strong>CI/CD system</strong>.</p>
<p><img src="https://web.af-site.amifactory.dev/blog/content/images/2019/05/Blog-illustration-5.png" alt="How to deploy a website?" loading="lazy"></p>
<p>Let&apos;s dive a little in the theory. <strong>CI</strong>, Continuous integration - is the practice of merging all developers working copies to a shared mainline several times a day. (from <a href="https://en.wikipedia.org/wiki/Continuous_integration?ref=web.af-site.amifactory.dev">Wikipedia</a>). This practice is intended to solve the problem of integration hell. Working on the project alone, this problem may never occur. But working on several working stations (work and home laptop for example) - be sure, sooner or later you will encounter this problem. Having the central repository in your workflow, which is a single actual source of the codebase, allows you to avoid this problem and become the basis for the implementation of the CI practice.</p>
<p>The role of the central repository might play repository on GitHub/GitLab/in-house git repository. It doesn&apos;t matter where the repository is hosted, it is important that you treat it as &apos;central&apos; repository. Having a &apos;central&apos; repository and synchronizing your codebase with it, we solve the first task: the synchronization of the code base.</p>
<p>Now a few words about CD. <strong>CD</strong> might mean two things:</p>
<ul>
<li>Continuous delivery - is a software engineering approach in which teams produce software in short cycles, ensuring that the software can be reliably released at any time and, when releasing the software, doing so manually (from <a href="https://en.wikipedia.org/wiki/Continuous_delivery?ref=web.af-site.amifactory.dev">Wikipedia</a>).</li>
<li>Continuous deployment -  is a software engineering approach in which software functionalities are delivered frequently through automated deployments (from <a href="https://en.wikipedia.org/wiki/Continuous_deployment?ref=web.af-site.amifactory.dev">Wikipedia</a>).</li>
</ul>
<p><mark>So, the task of Continuous Delivery ensures the readiness of the product for deploying.</mark> In our case - the website. <mark>And Continuous Deployment provides an automatic deploy of ready-to-publish build.</mark> Under the readiness, we mean the successful execution of additional steps to get the prepared build of the website: run preprocessors, linters, minimization, obfuscation, packaging, testing, containerization, etc. Upon completion of the building, the process of deploying begins. The pipeline described above can trigger both when making changes to the central repository, and manually.</p>
<p>Let&apos;s get back to practice. we will use <a href="https://about.gitlab.com/?ref=web.af-site.amifactory.dev">GitLab</a> as a service for hosting the central repository and CI/CD system. Why not GitHub? There are several reasons:</p>
<ul>
<li>GitLab is an all-in-one service: Git repositories, CI/CD pipelines, issue tracker, Docker Registry, Simple Artifactory and many more. All these functionalities are well integrated with each other. At the time of writing this post, GitHub doesn&apos;t have similar functionality and such integrations. GitHub provides good integrations with many third-party CI/CD services, but do we really need to use any additional third-party if we can avoid it?</li>
<li>GitLab has a free plan for self-hosted installations. This type of installation is important for service/outsourcing companies that work with the private customer code. In any case, we are responsible for the possible unauthorized distribution of the codebase so we prefer to avoid using any additional third-party SaaS in our processes, especially if we have to trust codebase to them. From the other side, the self-hosted solution allows us to restrict the access of any third parties and unauthorized users to the codebase and sensitive data (passwords, keys, certs, etc). In this case, the level of security and protection depends only on our team. Yes, I understand that we may make mistakes, but this will be our mistakes and our responsibility.</li>
<li>BitBucket also offers a self-hosted installation, but BitBucket does not have free plans for a self-hosted installation and a little poorer functionality (it&apos;s my personal thoughts are based on my experience of mid-2018);</li>
<li>As mentioned above, GitLab has built-in <a href="https://about.gitlab.com/product/continuous-integration/?ref=web.af-site.amifactory.dev">CI/CD pipelines</a>. This eliminates the need to use third-party services such as CircleCI or TravisCI, regardless of whether the SaaS or Self-hosted installation is used. No, I&apos;m not against third-party SaaS. My idea is to use the right tool/service based on the task requirements and the need to minimize the risks.</li>
</ul>
<p>For this guide, we will use <a href="https://gitlab.com/?ref=web.af-site.amifactory.dev">GitLab InCloud</a> so you need to have the account on <a href="https://gitlab.com/?ref=web.af-site.amifactory.dev">gitlab.com</a>.<br>
Sign-In/Sign-Up here <a href="https://gitlab.com/users/sign_in?ref=web.af-site.amifactory.dev#register-pane">https://gitlab.com/users/sign_in#register-pane</a></p>
<p><img src="https://web.af-site.amifactory.dev/blog/content/images/2019/05/Blog-illustration-6.png" alt="How to deploy a website?" loading="lazy"></p>
<p>Once you have successfully logged in to your account, you need to create a new Project, which will contain a central repository. The project may be associated with your username, but GitLab provides the ability to create a Group for the Project. This is convenient if you need to group Projects or the Project can potentially grow into several repositories.</p>
<p><img src="https://web.af-site.amifactory.dev/blog/content/images/2019/05/Blog-illustration-7.png" alt="How to deploy a website?" loading="lazy"></p>
<p>After creating the repository, we get to the landing page of the project. We are interested in two sections: &quot;Push an existing folder&quot; and &quot;Push an existing Git repository&quot;.</p>
<p><img src="https://web.af-site.amifactory.dev/blog/content/images/2019/05/Blog-illustration-8.png" alt="How to deploy a website?" loading="lazy"></p>
<p>Depending on whether your project is in under version control or not, you need to execute one of the available sequences.</p>
<p>For a project, that didn&#x2019;t use <code>git</code> before:</p>
<pre><code>cd existing_folder
git init
git remote add origin git@gitlab.com:AmiFactoryTeam/my-awesome-website.git
git add .
git commit -m &quot;Initial commit&quot;
git push -u origin master
</code></pre>
<p>For a project under <code>git</code> and WITHOUT existing central repository:</p>
<pre><code>cd existing_repo
git remote add origin git@gitlab.com:AmiFactoryTeam/test.git
git push -u origin --all
git push -u origin --tags
</code></pre>
<p>For a project under <code>git</code> and WITH replacing an existing central repository:</p>
<pre><code>cd existing_repo
git remote rename origin old-origin
git remote add origin git@gitlab.com:AmiFactoryTeam/test.git
git push -u origin --all
git push -u origin --tags
</code></pre>
<p>For a project under <code>git</code> and WITHOUT replacing an existing central repository:</p>
<pre><code>cd existing_repo
git remote add gitlab git@gitlab.com:AmiFactoryTeam/test.git
git push -u gitlab --all
git push -u gitlab --tags
</code></pre>
<p>So as a result, we&apos;ve pushed a working copy of the project into the central repository.</p>
<p>Now we are going to describe our <a href="https://docs.gitlab.com/ee/ci/pipelines.html?ref=web.af-site.amifactory.dev">GitLab CI/CD Pipeline</a>  with two Stages: build and deploy. For each Stage we will describe Job. Job description contains configuration params and execution steps - shell commands.</p>
<p>Let&apos;s create <code>.gitlab-ci.yml</code> file on the repository root. This file will contain Pipeline configuration:</p>
<pre><code>stages:
  - build
  - deploy

build_job:
  stage: build
  image: node:8.16.0-alpine
  script:
    - apk --update add zip
    - npm i
    - npm run build
    - zip -r dist.zip dist/
  artifacts:
    paths:
      - dist.zip
    expire_in: 1 month

deploy_job:
  stage: deploy
  image: alpine:3.9
  environment:
    name: production
  variables:
    MAW_HOST_VHOST_SITE_ROOT: &quot;/opt/webserver/www/myawesomewebsite.afstaging.network&quot;
  script:
    - apk --update add openssh-client 
    - eval $(ssh-agent -s)
    - echo &quot;$MAW_HOST_SSH_PRIVATE_KEY&quot; | tr -d &apos;\r&apos; | ssh-add - &gt; /dev/null
    - mkdir -p ~/.ssh &amp;&amp; chmod 700 ~/.ssh
    - ssh-keyscan -t rsa -H $MAW_HOST &gt;&gt; ~/.ssh/known_hosts &amp;&amp; chmod 644 ~/.ssh/known_hosts
    - scp dist.zip $MAW_HOST_USER@$MAW_HOST:/tmp/
    - ssh &quot;$MAW_HOST_USER@$MAW_HOST&quot; &apos;bash -s&apos; &lt; misc/deploy/deploy.sh $MAW_HOST_VHOST_SITE_ROOT
</code></pre>
<p>You can read the full documentation for <code>.gitlab-ci.yml</code> <a href="https://docs.gitlab.com/ee/ci/yaml/?ref=web.af-site.amifactory.dev">here</a>, but I&apos;ll provide a short description of the current configuration:</p>
<ul>
<li>
<p><code>stages</code> - list of Pipeline Stages: <code>build</code> and <code>deploy</code>;</p>
</li>
<li>
<p><code>build_job:</code> - name and start of the first Job description. Its purpose is to prepare our project to deployment. Job configuration:</p>
<ul>
<li><code>stage: build</code> - declare that this Job is a <code>build</code> Stage of Pipeline;</li>
<li><code>image: node:8.16.0-alpine</code> - docker image name + tag. This Job runs in a docker container, which runs from the specified image. The docker image is based on Alpine Linux image with NodeJS installed;</li>
<li><code>script:</code> - here comes a list of commands, which will be run during Job execution. Because of GitLab Runner runs on Linux, this is similar to a plain shell script:
<ul>
<li><code>apk --update add zip</code> - install zip tool to zip our <code>dest</code> directory before transfer to the remote host. <code>apk</code> is default package manager for Alpine Linux;</li>
<li><code>npm i, npm run build</code> - install project dependencies and run build process;</li>
<li><code>zip -r dist.zip dist/</code> - zip dist directory.</li>
</ul>
</li>
<li><code>artifacts:</code> - the list of <a href="https://docs.gitlab.com/ee/user/project/pipelines/job_artifacts.html?ref=web.af-site.amifactory.dev#introduction-to-job-artifacts">Artifacts</a>, basically resulting products of our Job. They will be used by <code>deploy_job</code>. Here we specify which files/dirs should be saved for the future use and their expiration time.</li>
</ul>
</li>
<li>
<p><code>deploy_job:</code> - name and start of the second Job description. Its purpose is to deploy our project. Job configuration:</p>
<ul>
<li><code>stage: deploy</code> - similar to the first Job, we declare that this Job is a <code>deploy</code> Stage of Pipeline;</li>
<li><code>image: alpine:3.9</code> - docker image name + tag. This Job runs in a docker container too. The specified image is Alpine Linux v3.9.x;</li>
<li><code>environment</code> - here we specify <a href="https://docs.gitlab.com/ee/ci/environments.html?ref=web.af-site.amifactory.dev">Environment</a> name in which we deploy our site. Environment name might be any you like, but it should reflect the aim of Environment, for example: <code>development</code>, <code>staging</code>, <code>production</code>, <code>live</code>;</li>
<li><code>variables:</code> - here we describe a list of variables, that will be used in our deploy script;</li>
<li><code>script:</code> - here comes a list of commands, which will be run during Job execution:
<ul>
<li><code>apk --update add openssh-client</code> - install ssh client to have ability connecting to the remote host;</li>
<li><code>eval $(ssh-agent -s)</code><br>
<code>echo &quot;$MAW_HOST_SSH_PRIVATE_KEY&quot; | tr -d &apos;\r&apos; | ssh-add - &gt; /dev/null</code> - run ssh agend as a deamon and add remote host private key to it;</li>
<li><code>mkdir -p ~/.ssh &amp;&amp; chmod 700 ~/.ssh</code><br>
<code>ssh-keyscan -t rsa -H $MAW_HOST &gt;&gt; ~/.ssh/known_hosts &amp;&amp; chmod 644 ~/.ssh/known_hosts</code> - use <a href="https://linux.die.net/man/1/ssh-keyscan?ref=web.af-site.amifactory.dev"><strong>ssh-keyscan</strong></a> to gather public keys of the remote host and place it to <code>known_hosts</code> file</li>
<li><code>scp dist.zip $MAW_HOST_USER@$MAW_HOST:/tmp/</code> - use <code>scp</code> to move zipped build to the remote host;</li>
<li><code>ssh &quot;$MAW_HOST_USER@$MAW_HOST&quot; &apos;bash -s&apos; &lt; misc/deploy/deploy.sh $MAW_HOST_VHOST_SITE_ROOT</code> - execute deploy script on remote host to unzip, backup and update website.</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><code>MAW_HOST</code>, <code>MAW_HOST_USER</code> and <code>MAW_HOST_SSH_PRIVATE_KEY</code> are <a href="https://docs.gitlab.com/ee/ci/variables/?ref=web.af-site.amifactory.dev#via-the-ui">GitLab Variables</a>. You need to set them up under Nav menu&gt;Settings&gt;CI/CD before the first Pipeline run because they use during Job execution:</p>
<p><img src="https://web.af-site.amifactory.dev/blog/content/images/2019/05/Blog-illustration-14.png" alt="How to deploy a website?" loading="lazy"></p>
<p>Commit <code>.gitlab-ci.yml</code> file in repository and push changes.</p>
<p>Go to the project&apos;s <strong>Pipelines list</strong> (Nav nenu-&gt;CI&gt;CD-&gt;Pipelines). You will see that the first Pipeline of this project has started. Pipeline consists of two Jobs, described previously in <code>.gitlab-ci.yml</code>: Build and Deploy respectively. Each Job contains a job execution log and brief information such as execution time, used runner and other.</p>
<p>As soon as all Jobs of Pipeline finish successfully - Pipeline is considered successfully finished. Unsuccessful Pipeline, or its specific Job, can be repeated. This is useful in cases where Job has fallen due to a problem with external factors, like when dependencies of the project could not be downloaded or the remote host might temporarily not available.</p>
<p>See below a few screens from our <a href="https://gitlab.com/AmiFactoryTeam/my-awesome-website?ref=web.af-site.amifactory.dev">demo project</a>.</p>
<p>The list of <a href="https://gitlab.com/AmiFactoryTeam/my-awesome-website/pipelines?ref=web.af-site.amifactory.dev">Pipelines</a>:</p>
<p><img src="https://web.af-site.amifactory.dev/blog/content/images/2019/05/Blog-illustration-9.png" alt="How to deploy a website?" loading="lazy"></p>
<p>We see that the project has one failed, one passed and one running Pipeline. Also, you can notice, that the first Pipiline has failed on the second Stage - <code>deploy_job</code> and the third is currently running the first Stage - <code>build_job</code>. You can click on <a href="https://gitlab.com/AmiFactoryTeam/my-awesome-website/pipelines/61009169?ref=web.af-site.amifactory.dev">pipeline state label</a> to open Pipiline details:</p>
<p><img src="https://web.af-site.amifactory.dev/blog/content/images/2019/05/Blog-illustration-10.png" alt="How to deploy a website?" loading="lazy"></p>
<p>Here you can click on <a href="https://gitlab.com/AmiFactoryTeam/my-awesome-website/-/jobs/211371770?ref=web.af-site.amifactory.dev">each Stage</a> to see it details and run log:</p>
<p><img src="https://web.af-site.amifactory.dev/blog/content/images/2019/05/Blog-illustration-11.png" alt="How to deploy a website?" loading="lazy"></p>
<p>This is how GitLab helps visualize and provide information about CI/CD process.</p>
<p>Let&apos;s say a few words <a href="https://docs.gitlab.com/ee/ci/environments.html?ref=web.af-site.amifactory.dev">GitLab Environments</a>. Environment is the abstraction for a project running environments, like staging or production. Environment saves information about the last successful deployment and the history of all previously deployments with the ability to quickly rollback to any past deployment. Here the list of Environments for our demo project:</p>
<p><img src="https://web.af-site.amifactory.dev/blog/content/images/2019/05/Blog-illustration-12.png" alt="How to deploy a website?" loading="lazy"></p>
<p>As you can see, we have the only one Environment <code>production</code>, described in <code>.gitlab-ci.yml</code> file. Clicking on the Environment name, we see the history of project deployments:</p>
<p><img src="https://web.af-site.amifactory.dev/blog/content/images/2019/05/Blog-illustration-13.png" alt="How to deploy a website?" loading="lazy"></p>
<p>Buttons on the right run re-deployment for the deployment #3 and rollback to previous Environment state for deployment #2.</p>
<h4 id="pros">Pros:</h4>
<ul>
<li>Full automation of building and deploy processes;</li>
<li>Flexible and declarative way of describing each stage of the process in the <code>.gitlab-ci.yml</code> file;</li>
<li>Flexible way to set up process triggers. Pipeline launch can be triggered by a commit on a defined branch, merge request, etc.;</li>
<li>Deploy can be launched automatically after a successful build, or manually, thought &apos;Run&apos; button;</li>
<li>Ability to quickly rollback unsuccessful deployments;</li>
<li>History and logs of builds and deployments;</li>
<li>The CI/CD script code is located in the git repository;</li>
<li>The developer may not have direct access to the remote host;</li>
</ul>
<h4 id="cons">Cons:</h4>
<ul>
<li>Changing CI/CD configuration requires changes to <code>.gitlab-ci.yml</code> file with new commit to a repository;</li>
<li>Deploy jobs need to have access to the remote host. This requires saving the private ssh key in CI/CD secret variables. It&apos;s a typical approach for this kind of systems and not a big problem in the case of self-hosted installs.</li>
</ul>
<p>Another minor disadvantage of the described method is the fact that GitLab is less popular than GitHub. GitHub is the de-facto standard both for opensource projects and product and service companies. Therefore, to use the described method, especially for exists projects, you will need migration or mirroring from GitHub to GitLab repository. Both options are poorly fit for existing projects, so this method is well suited for new projects, rather than for existing ones.</p>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><h2 id="summary">Summary</h2>
<p>Let&apos;s summarize. The deploy of the modern website can be divided into several phases. There are at least two main stages: preparation steps execution, or build and the actual deployment to the remote server. In the simplest case, the first stage is executed manually and then with the help of scp, rsync or FTP, the files are transferred to the remote host. In case the project uses Git, it is possible to implement the deployment using git-hooks. For more complex projects, where the number of developers &gt;1 is a good option is to use CI/CD techniques and tools like GitLab CI/CD pipelines. Always use the right tool for each task and happy deployment!</p>
<p><em>P.S. If I have interested you in using GitLab and features like CI/CD Pipelines and Environments or you want to know more on how we build GitLab-based automation at AmiFactory - let me know in comments! We are always glad to hear your feedback.</em></p>
<!--kg-card-end: markdown-->]]></content:encoded></item></channel></rss>