<?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: Bruno Verachten</title>
    <description>The latest articles on DEV Community by Bruno Verachten (@gounthar).</description>
    <link>https://dev.to/gounthar</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%2F119512%2F56f3a9de-2488-4302-ae9a-a97b3b037299.jpeg</url>
      <title>DEV Community: Bruno Verachten</title>
      <link>https://dev.to/gounthar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gounthar"/>
    <language>en</language>
    <item>
      <title>Compile FFmpeg on the Odroid XU4 to get hardware acceleration</title>
      <dc:creator>Bruno Verachten</dc:creator>
      <pubDate>Wed, 22 Apr 2026 07:44:26 +0000</pubDate>
      <link>https://dev.to/gounthar/compile-ffmpeg-on-the-odroid-xu4-to-get-hardware-acceleration-14b8</link>
      <guid>https://dev.to/gounthar/compile-ffmpeg-on-the-odroid-xu4-to-get-hardware-acceleration-14b8</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on November 28, 2019 at &lt;a href="https://bruno.verachten.fr/2019/11/28/ffmpeg-for-xu4/" rel="noopener noreferrer"&gt;https://bruno.verachten.fr/2019/11/28/ffmpeg-for-xu4/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;






&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get build-dep ffmpeg

&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;debhelper libchromaprint-dev frei0r-plugins-dev libgnutls28-dev libx265-dev ladspa-sdk libaom-dev libserd-dev libsord-dev libsratom-dev libsndfile1-dev libiec61883-dev libavc1394-dev libavc1394-0 libass-dev libbluray-dev libbs2b-dev libcaca-dev

 &lt;span class="nb"&gt;mkdir &lt;/span&gt;ffmpeg_sources
   36  &lt;span class="nb"&gt;cd &lt;/span&gt;ffmpeg_sources
   37  wget http://deb.debian.org/debian/pool/main/f/ffmpeg/ffmpeg_4.1.4.orig.tar.xz
   38  xz &lt;span class="nt"&gt;-d&lt;/span&gt; ffmpeg_4.1.4.orig.tar.xz
   39  &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xf&lt;/span&gt; ffmpeg_4.1.4.orig.tar
   40  wget http://deb.debian.org/debian/pool/main/f/ffmpeg/ffmpeg_4.1.4.orig.tar.xz
   41  ll
   42  &lt;span class="nb"&gt;cd &lt;/span&gt;ffmpeg-4.1.4/
   43  wget http://deb.debian.org/debian/pool/main/f/ffmpeg/ffmpeg_4.1.4-1~deb10u1.debian.tar.xz
   44  xz &lt;span class="nt"&gt;-d&lt;/span&gt; ffmpeg_4.1.4-1~deb10u1.debian.tar.xz
   45  ll
   46  &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xf&lt;/span&gt; ffmpeg_4.1.4-1~deb10u1.debian.tar
   47  ll
   48  &lt;span class="nb"&gt;rm &lt;/span&gt;ffmpeg_4.1.4-1~deb10u1.debian.tar

&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; dpkg-buildpackage &lt;span class="nt"&gt;-us&lt;/span&gt; &lt;span class="nt"&gt;-uc&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;

ffmpeg &lt;span class="nt"&gt;-i&lt;/span&gt; bbb_sunflower_1080p_30fps_stereo_abl.mp4 &lt;span class="nt"&gt;-an&lt;/span&gt; &lt;span class="nt"&gt;-vcodec&lt;/span&gt; h264_v4l2m2m &lt;span class="nt"&gt;-b&lt;/span&gt;:v 2M &lt;span class="nt"&gt;-pix_fmt&lt;/span&gt; nv21 output.mp4
ffmpeg version 4.1.4-1~deb10u1 Copyright &lt;span class="o"&gt;(&lt;/span&gt;c&lt;span class="o"&gt;)&lt;/span&gt; 2000-2019 the FFmpeg developers
  built with gcc 8 &lt;span class="o"&gt;(&lt;/span&gt;Debian 8.3.0-6&lt;span class="o"&gt;)&lt;/span&gt;
  configuration: &lt;span class="nt"&gt;--prefix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr &lt;span class="nt"&gt;--extra-version&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'1~deb10u1'&lt;/span&gt; &lt;span class="nt"&gt;--toolchain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;hardened &lt;span class="nt"&gt;--libdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/lib/arm-linux-gnueabihf &lt;span class="nt"&gt;--incdir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/usr/include/arm-linux-gnueabihf &lt;span class="nt"&gt;--arch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;arm &lt;span class="nt"&gt;--enable-gpl&lt;/span&gt; &lt;span class="nt"&gt;--disable-stripping&lt;/span&gt; &lt;span class="nt"&gt;--enable-avresample&lt;/span&gt; &lt;span class="nt"&gt;--disable-filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;resample &lt;span class="nt"&gt;--enable-avisynth&lt;/span&gt; &lt;span class="nt"&gt;--enable-gnutls&lt;/span&gt; &lt;span class="nt"&gt;--enable-ladspa&lt;/span&gt; &lt;span class="nt"&gt;--enable-libaom&lt;/span&gt; &lt;span class="nt"&gt;--enable-libass&lt;/span&gt; &lt;span class="nt"&gt;--enable-libbluray&lt;/span&gt; &lt;span class="nt"&gt;--enable-libbs2b&lt;/span&gt; &lt;span class="nt"&gt;--enable-libcaca&lt;/span&gt; &lt;span class="nt"&gt;--enable-libcdio&lt;/span&gt; &lt;span class="nt"&gt;--enable-libcodec2&lt;/span&gt; &lt;span class="nt"&gt;--enable-libflite&lt;/span&gt; &lt;span class="nt"&gt;--enable-libfontconfig&lt;/span&gt; &lt;span class="nt"&gt;--enable-libfreetype&lt;/span&gt; &lt;span class="nt"&gt;--enable-libfribidi&lt;/span&gt; &lt;span class="nt"&gt;--enable-libgme&lt;/span&gt; &lt;span class="nt"&gt;--enable-libgsm&lt;/span&gt; &lt;span class="nt"&gt;--enable-libjack&lt;/span&gt; &lt;span class="nt"&gt;--enable-libmp3lame&lt;/span&gt; &lt;span class="nt"&gt;--enable-libmysofa&lt;/span&gt; &lt;span class="nt"&gt;--enable-libopenjpeg&lt;/span&gt; &lt;span class="nt"&gt;--enable-libopenmpt&lt;/span&gt; &lt;span class="nt"&gt;--enable-libopus&lt;/span&gt; &lt;span class="nt"&gt;--enable-libpulse&lt;/span&gt; &lt;span class="nt"&gt;--enable-librsvg&lt;/span&gt; &lt;span class="nt"&gt;--enable-librubberband&lt;/span&gt; &lt;span class="nt"&gt;--enable-libshine&lt;/span&gt; &lt;span class="nt"&gt;--enable-libsnappy&lt;/span&gt; &lt;span class="nt"&gt;--enable-libsoxr&lt;/span&gt; &lt;span class="nt"&gt;--enable-libspeex&lt;/span&gt; &lt;span class="nt"&gt;--enable-libssh&lt;/span&gt; &lt;span class="nt"&gt;--enable-libtheora&lt;/span&gt; &lt;span class="nt"&gt;--enable-libtwolame&lt;/span&gt; &lt;span class="nt"&gt;--enable-libvidstab&lt;/span&gt; &lt;span class="nt"&gt;--enable-libvorbis&lt;/span&gt; &lt;span class="nt"&gt;--enable-libvpx&lt;/span&gt; &lt;span class="nt"&gt;--enable-libwavpack&lt;/span&gt; &lt;span class="nt"&gt;--enable-libwebp&lt;/span&gt; &lt;span class="nt"&gt;--enable-libx265&lt;/span&gt; &lt;span class="nt"&gt;--enable-libxml2&lt;/span&gt; &lt;span class="nt"&gt;--enable-libxvid&lt;/span&gt; &lt;span class="nt"&gt;--enable-libzmq&lt;/span&gt; &lt;span class="nt"&gt;--enable-libzvbi&lt;/span&gt; &lt;span class="nt"&gt;--enable-lv2&lt;/span&gt; &lt;span class="nt"&gt;--enable-omx&lt;/span&gt; &lt;span class="nt"&gt;--enable-openal&lt;/span&gt; &lt;span class="nt"&gt;--enable-opengl&lt;/span&gt; &lt;span class="nt"&gt;--enable-sdl2&lt;/span&gt; &lt;span class="nt"&gt;--enable-libdc1394&lt;/span&gt; &lt;span class="nt"&gt;--enable-libdrm&lt;/span&gt; &lt;span class="nt"&gt;--enable-libiec61883&lt;/span&gt; &lt;span class="nt"&gt;--enable-chromaprint&lt;/span&gt; &lt;span class="nt"&gt;--enable-frei0r&lt;/span&gt; &lt;span class="nt"&gt;--enable-libx264&lt;/span&gt; &lt;span class="nt"&gt;--enable-shared&lt;/span&gt;
  libavutil      56. 22.100 / 56. 22.100
  libavcodec     58. 35.100 / 58. 35.100
  libavformat    58. 20.100 / 58. 20.100
  libavdevice    58.  5.100 / 58.  5.100
  libavfilter     7. 40.101 /  7. 40.101
  libavresample   4.  0.  0 /  4.  0.  0
  libswscale      5.  3.100 /  5.  3.100
  libswresample   3.  3.100 /  3.  3.100
  libpostproc    55.  3.100 / 55.  3.100
Input &lt;span class="c"&gt;#0, mov,mp4,m4a,3gp,3g2,mj2, from 'bbb_sunflower_1080p_30fps_stereo_abl.mp4':&lt;/span&gt;
  Metadata:
    major_brand     : isom
    minor_version   : 1
    compatible_brands: isomavc1
    creation_time   : 2013-12-16T17:46:34.000000Z
    title           : Big Buck Bunny, Sunflower version
    artist          : Blender Foundation 2008, Janus Bager Kristensen 2013
    comment         : Creative Commons Attribution 3.0 - http://bbb3d.renderfarming.net
    genre           : Animation
    composer        : Sacha Goedegebure
  Duration: 00:10:34.53, start: 0.000000, bitrate: 5482 kb/s
    Stream &lt;span class="c"&gt;#0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x2160 [SAR 1:1 DAR 8:9], 4999 kb/s, 30 fps, 30 tbr, 30k tbn, 60 tbc (default)&lt;/span&gt;
    Metadata:
      creation_time   : 2013-12-16T17:46:34.000000Z
      handler_name    : GPAC ISO Video Handler
    Stream &lt;span class="c"&gt;#0:1(und): Audio: mp3 (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 160 kb/s (default)&lt;/span&gt;
    Metadata:
      creation_time   : 2013-12-16T17:46:41.000000Z
      handler_name    : GPAC ISO Audio Handler
    Stream &lt;span class="c"&gt;#0:2(und): Audio: ac3 (ac-3 / 0x332D6361), 48000 Hz, 5.1(side), fltp, 320 kb/s (default)&lt;/span&gt;
    Metadata:
      creation_time   : 2013-12-16T17:46:41.000000Z
      handler_name    : GPAC ISO Audio Handler
    Side data:
      audio service &lt;span class="nb"&gt;type&lt;/span&gt;: main
Stream mapping:
  Stream &lt;span class="c"&gt;#0:0 -&amp;gt; #0:0 (h264 (native) -&amp;gt; h264 (h264_v4l2m2m))&lt;/span&gt;
Press &lt;span class="o"&gt;[&lt;/span&gt;q] to stop, &lt;span class="o"&gt;[&lt;/span&gt;?] &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="nb"&gt;help&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;h264_v4l2m2m @ 0x575a90] driver &lt;span class="s1"&gt;'exynos-gsc'&lt;/span&gt; on card &lt;span class="s1"&gt;'exynos-gsc gscaler'&lt;/span&gt;
    Last message repeated 1 &lt;span class="nb"&gt;times&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;h264_v4l2m2m @ 0x575a90] driver &lt;span class="s1"&gt;'s5p-mfc'&lt;/span&gt; on card &lt;span class="s1"&gt;'s5p-mfc-enc'&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;h264_v4l2m2m @ 0x575a90] Using device /dev/video11
&lt;span class="o"&gt;[&lt;/span&gt;h264_v4l2m2m @ 0x575a90] driver &lt;span class="s1"&gt;'s5p-mfc'&lt;/span&gt; on card &lt;span class="s1"&gt;'s5p-mfc-enc'&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;h264_v4l2m2m @ 0x575a90] h264 profile not found
&lt;span class="o"&gt;[&lt;/span&gt;h264_v4l2m2m @ 0x575a90] Encoder adjusted: qmin &lt;span class="o"&gt;(&lt;/span&gt;0&lt;span class="o"&gt;)&lt;/span&gt;, qmax &lt;span class="o"&gt;(&lt;/span&gt;51&lt;span class="o"&gt;)&lt;/span&gt;
Output &lt;span class="c"&gt;#0, mp4, to 'output.mp4':&lt;/span&gt;
  Metadata:
    major_brand     : isom
    minor_version   : 1
    compatible_brands: isomavc1
    composer        : Sacha Goedegebure
    title           : Big Buck Bunny, Sunflower version
    artist          : Blender Foundation 2008, Janus Bager Kristensen 2013
    comment         : Creative Commons Attribution 3.0 - http://bbb3d.renderfarming.net
    genre           : Animation
    encoder         : Lavf58.20.100
    Stream &lt;span class="c"&gt;#0:0(und): Video: h264 (h264_v4l2m2m) (avc1 / 0x31637661), nv21, 1920x2160 [SAR 1:1 DAR 8:9], q=2-31, 2000 kb/s, 30 fps, 15360 tbn, 30 tbc (default)&lt;/span&gt;
    Metadata:
      creation_time   : 2013-12-16T17:46:34.000000Z
      handler_name    : GPAC ISO Video Handler
      encoder         : Lavc58.35.100 h264_v4l2m2m
&lt;span class="o"&gt;[&lt;/span&gt;mp4 @ 0x5695f0] Non-monotonous DTS &lt;span class="k"&gt;in &lt;/span&gt;output stream 0:0&lt;span class="p"&gt;;&lt;/span&gt; previous: 0, current: 0&lt;span class="p"&gt;;&lt;/span&gt; changing to 1. This may result &lt;span class="k"&gt;in &lt;/span&gt;incorrect timestamps &lt;span class="k"&gt;in &lt;/span&gt;the output file.
&lt;span class="o"&gt;[&lt;/span&gt;mp4 @ 0x5695f0] Non-monotonous DTS &lt;span class="k"&gt;in &lt;/span&gt;output stream 0:0&lt;span class="p"&gt;;&lt;/span&gt; previous: 832000, current: 0&lt;span class="p"&gt;;&lt;/span&gt; changing to 832001. This may result &lt;span class="k"&gt;in &lt;/span&gt;incorrect timestamps &lt;span class="k"&gt;in &lt;/span&gt;the output file.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>armbian</category>
      <category>ffmpeg</category>
      <category>h264</category>
      <category>odroid</category>
    </item>
    <item>
      <title>Terminal recording on the OrangePi Zero</title>
      <dc:creator>Bruno Verachten</dc:creator>
      <pubDate>Wed, 22 Apr 2026 07:44:23 +0000</pubDate>
      <link>https://dev.to/gounthar/terminal-recording-on-the-orangepi-zero-aad</link>
      <guid>https://dev.to/gounthar/terminal-recording-on-the-orangepi-zero-aad</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on January 3, 2020 at &lt;a href="https://bruno.verachten.fr/2020/01/03/terminal-recording-on-the-orangepi-zero/" rel="noopener noreferrer"&gt;https://bruno.verachten.fr/2020/01/03/terminal-recording-on-the-orangepi-zero/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Terminal recording on the OrangePi Zero
&lt;/h1&gt;

&lt;p&gt;Lately, I started writing a few articles on the OrangePi &lt;a href="http://www.orangepi.org/orangepizerolts/" rel="noopener noreferrer"&gt;Zero&lt;/a&gt;, as I am trying to use it as part of a conference recording tool and to reference what I know about this board on the OrangePi Community docs &lt;a href="https://www.sbc-community.org/" rel="noopener noreferrer"&gt;website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Of course, I have to show my command lines, and their results.&lt;/p&gt;

&lt;p&gt;I used to post my commands on &lt;a href="https://gist.github.com/gounthar" rel="noopener noreferrer"&gt;gist&lt;/a&gt;, then use link:&lt;a href="https://carbon.now.sh/?bg=rgba(171%2C" rel="noopener noreferrer"&gt;https://carbon.now.sh/?bg=rgba(171%2C&lt;/a&gt; 184%2C 195%2C 1[Carbon]&amp;amp;t=seti&amp;amp;wt=none&amp;amp;l=auto&amp;amp;ds=true&amp;amp;dsyoff=20px&amp;amp;dsblur=68px&amp;amp;wc=true&amp;amp;wa=true&amp;amp;pv=56px&amp;amp;ph=56px&amp;amp;ln=false&amp;amp;fm=Hack&amp;amp;fs=14px&amp;amp;lh=133%&amp;amp;si=false&amp;amp;es=2x&amp;amp;wm=false) to generate an image with the commands and their outputs… But that’s quite a lot of boring work…&lt;/p&gt;

&lt;p&gt;I then discovered &lt;a href="https://asciinema.org/" rel="noopener noreferrer"&gt;asciinema&lt;/a&gt; and the recording that can be transformed into a GIF file thanks to &lt;a href="https://github.com/asciinema/asciicast2gif?source=post_page---------------------------" rel="noopener noreferrer"&gt;asciicast2gif&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It worked pretty well under Ubuntu for my previous projects, but it was not really practical to launch it, issue an ssh command, and then exit twice before having the recording… and modifying it to get rid of the SSH command.&lt;/p&gt;

&lt;p&gt;Furthermore, GIF are not usable for visually impaired people, so using something like &lt;a href="https://en.wikipedia.org/wiki/Scalable_Vector_Graphics" rel="noopener noreferrer"&gt;SVG &lt;/a&gt;where they could copy and paste would be better in my opinion…&lt;/p&gt;

&lt;p&gt;Unfortunately, SVG is not handled by medium, so it will have to be used on other media, like my Ghost instance (not available yet).&lt;/p&gt;

&lt;p&gt;Enters &lt;a href="https://github.com/nbedos/termtosvg" rel="noopener noreferrer"&gt;termtosvg&lt;/a&gt;. That’s a wonderful tool aimed at producing animated SVG after recording terminal sessions.&lt;/p&gt;

&lt;p&gt;It compiled like a breeze (well, a strong, smelly and cold breeze) on the Zero, so I used it quite intensively (before discovering it could not be used on Medium).&lt;/p&gt;

&lt;p&gt;I then searched for tools able to transform an animated SVG to &lt;a href="https://en.wikipedia.org/wiki/GIF" rel="noopener noreferrer"&gt;GIF&lt;/a&gt;, so that I would not have to redo all my terminal recordings… and was not able to do so.&lt;/p&gt;

&lt;p&gt;There is one library called &lt;a href="https://github.com/GNOME/librsvg" rel="noopener noreferrer"&gt;librsvg &lt;/a&gt;that could be used by &lt;a href="https://github.com/rockchip-linux/ffmpeg" rel="noopener noreferrer"&gt;ffmpeg&lt;/a&gt;, but I have not been able to compile it on the Zero, and it’s just an hypothesis, there is no guarantee at all that it could be used to produce GIF files from animated SVG files.&lt;/p&gt;

&lt;p&gt;termtosvg can produce files than can be used by &lt;a href="https://github.com/asciinema/asciicast2gif?source=post_page---------------------------" rel="noopener noreferrer"&gt;asciinema &lt;/a&gt;and then asciicast2gif to produce gif files, but the process is cumbersome and does not work on the Zero. I had to use a X86 Linux machine to do it.&lt;/p&gt;

&lt;p&gt;Anyway, with this article, you won’t be able to produce GIF images starting from a terminal recording if you’re using exclusively a Zero, but beautiful animated SVG that you will be able to use on your blog, or anywhere else where animated SVG is supported.&lt;/p&gt;

&lt;p&gt;If you’re brave enough to also use an X86 machine, go to the end of this article, and take time to get married, get children before getting your first GIF file.&lt;/p&gt;

&lt;h1&gt;
  
  
  TermToSVG installation
&lt;/h1&gt;

&lt;h1&gt;
  
  
  TL;DR
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; http.proxy http://proxy-machine:proxy-port
&lt;span class="nb"&gt;sudo &lt;/span&gt;fallocate &lt;span class="nt"&gt;-l&lt;/span&gt; 1G /swapfile
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 /swapfile
&lt;span class="nb"&gt;sudo &lt;/span&gt;mkswap /swapfile
&lt;span class="nb"&gt;sudo &lt;/span&gt;swapon /swapfile
&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/fstab
git clone https://github.com/nbedos/termtosvg.git
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;python3-setuptools libxml2-dev libxslt-dev python3-dev python3-pip
pip3 &lt;span class="nb"&gt;install &lt;/span&gt;wheel
&lt;span class="nb"&gt;cd &lt;/span&gt;termtosvg
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Let's modify the Makefile so that we use python3 and pip3 as I can't get rid of Python2.7.x on this machine..."&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"See at https://gist.github.com/gounthar/b1870915710a0437b4e4eb8690e93553#file-makefile-patch"&lt;/span&gt;
make build
pip3 &lt;span class="nb"&gt;install &lt;/span&gt;dist/termtosvg-1.1.0-py3-none-any.whl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  The real installation process
&lt;/h1&gt;

&lt;p&gt;Let’s try to install termtosvg on the Zero. First of all, let’s configure the proxy if needed and then get the source code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;poddingue@orangepizero:~&lt;span class="nv"&gt;$ &lt;/span&gt;git clone https://github.com/nbedos/termtosvg.git
Cloning into &lt;span class="s1"&gt;'termtosvg'&lt;/span&gt;...
remote: Enumerating objects: 99, &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
remote: Counting objects: 100% &lt;span class="o"&gt;(&lt;/span&gt;99/99&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
remote: Compressing objects: 100% &lt;span class="o"&gt;(&lt;/span&gt;59/59&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
remote: Total 1592 &lt;span class="o"&gt;(&lt;/span&gt;delta 59&lt;span class="o"&gt;)&lt;/span&gt;, reused 68 &lt;span class="o"&gt;(&lt;/span&gt;delta 40&lt;span class="o"&gt;)&lt;/span&gt;, pack-reused 1493
Receiving objects: 100% &lt;span class="o"&gt;(&lt;/span&gt;1592/1592&lt;span class="o"&gt;)&lt;/span&gt;, 1.37 MiB | 2.08 MiB/s, &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Resolving deltas: 100% &lt;span class="o"&gt;(&lt;/span&gt;930/930&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
poddingue@orangepizero:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;termtosvg/
poddingue@orangepizero:~/termtosvg&lt;span class="nv"&gt;$ &lt;/span&gt;make build
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; dist &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
python setup.py sdist bdist_wheel
usage: setup.py &lt;span class="o"&gt;[&lt;/span&gt;global_opts] cmd1 &lt;span class="o"&gt;[&lt;/span&gt;cmd1_opts] &lt;span class="o"&gt;[&lt;/span&gt;cmd2 &lt;span class="o"&gt;[&lt;/span&gt;cmd2_opts] ...]
   or: setup.py &lt;span class="nt"&gt;--help&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;cmd1 cmd2 ...]
   or: setup.py &lt;span class="nt"&gt;--help-commands&lt;/span&gt;
   or: setup.py cmd &lt;span class="nt"&gt;--help&lt;/span&gt;

error: invalid &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="s1"&gt;'bdist_wheel'&lt;/span&gt;
make: &lt;span class="k"&gt;***&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;Makefile:31: build] Error 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course, the setuptools module can’t be found. I have not installed/configured Python2 setup tools. But as I had already done if for Python3, I modified the Makefile so that python is now python3, and pip is now pip3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nv"&gt;PIP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pip3
&lt;span class="nv"&gt;PYTHON&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;python3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I had not done it yet, it would have looked that way:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;poddingue@orangepizero:~/termtosvg$ sudo apt-get install python3-setuptools
Reading package lists... Done
Building dependency tree
Reading state information... Done
Suggested packages:
  python-setuptools-doc
The following NEW packages will be installed:
  python3-setuptools
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 306 kB of archives.
After this operation, 1,353 kB of additional disk space will be used.
Get:1 http://httpredir.debian.org/debian buster/main armhf python3-setuptools all 40.8.0-1 [306 kB]
Fetched 306 kB in 0s (1,225 kB/s)
Selecting previously unselected package python3-setuptools.
(Reading database ... 66711 files and directories currently installed.)
Preparing to unpack .../python3-setuptools_40.8.0-1_all.deb ...
Unpacking python3-setuptools (40.8.0-1) ...
Setting up python3-setuptools (40.8.0-1) ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now that Python setup tools are installed, let’s try it again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;poddingue@orangepizero:~/termtosvg&lt;span class="nv"&gt;$ &lt;/span&gt;make build
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; dist &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
python3 setup.py sdist bdist_wheel
usage: setup.py &lt;span class="o"&gt;[&lt;/span&gt;global_opts] cmd1 &lt;span class="o"&gt;[&lt;/span&gt;cmd1_opts] &lt;span class="o"&gt;[&lt;/span&gt;cmd2 &lt;span class="o"&gt;[&lt;/span&gt;cmd2_opts] ...]
   or: setup.py &lt;span class="nt"&gt;--help&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;cmd1 cmd2 ...]
   or: setup.py &lt;span class="nt"&gt;--help-commands&lt;/span&gt;
   or: setup.py cmd &lt;span class="nt"&gt;--help&lt;/span&gt;

error: invalid &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="s1"&gt;'bdist_wheel'&lt;/span&gt;
make: &lt;span class="k"&gt;***&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;Makefile:31: build] Error 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, fair enough, I’ll install wheel thanks to pip.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip3 &lt;span class="nb"&gt;install &lt;/span&gt;wheel
Collecting wheel
  Using cached https://files.pythonhosted.org/packages/00/83/b4a77d044e78ad1a45610eb88f745be2fd2c6d658f9798a15e384b7d57c9/wheel-0.33.6-py2.py3-none-any.whl
Installing collected packages: wheel
  The script wheel is installed &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s1"&gt;'/home/poddingue/.local/bin'&lt;/span&gt; which is not on PATH.
  Consider adding this directory to PATH or, &lt;span class="k"&gt;if &lt;/span&gt;you prefer to suppress this warning, use &lt;span class="nt"&gt;--no-warn-script-location&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Successfully installed wheel-0.33.6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then, as gently suggested by pip3:&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="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/home/poddingue/.local/bin:&lt;span class="nv"&gt;$PATH&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, let’s try it again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;poddingue@orangepizero:~/termtosvg&lt;span class="nv"&gt;$ &lt;/span&gt;make build
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; dist &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
python3 setup.py sdist bdist_wheel
running sdist
running egg_info
creating termtosvg.egg-info
writing termtosvg.egg-info/PKG-INFO
writing dependency_links to termtosvg.egg-info/dependency_links.txt
writing requirements to termtosvg.egg-info/requires.txt
writing top-level names to termtosvg.egg-info/top_level.txt
writing manifest file &lt;span class="s1"&gt;'termtosvg.egg-info/SOURCES.txt'&lt;/span&gt;
reading manifest file &lt;span class="s1"&gt;'termtosvg.egg-info/SOURCES.txt'&lt;/span&gt;
reading manifest template &lt;span class="s1"&gt;'MANIFEST.in'&lt;/span&gt;
warning: no previously-included files matching &lt;span class="s1"&gt;'__pycache__'&lt;/span&gt; found anywhere &lt;span class="k"&gt;in &lt;/span&gt;distribution
warning: no previously-included files matching &lt;span class="s1"&gt;'*.pyc'&lt;/span&gt; found anywhere &lt;span class="k"&gt;in &lt;/span&gt;distribution
warning: no files found matching &lt;span class="s1"&gt;'man/*.man.?'&lt;/span&gt;
writing manifest file &lt;span class="s1"&gt;'termtosvg.egg-info/SOURCES.txt'&lt;/span&gt;
running check
&lt;span class="o"&gt;[&lt;/span&gt;...]
Creating &lt;span class="nb"&gt;tar &lt;/span&gt;archive
removing &lt;span class="s1"&gt;'termtosvg-1.0.0'&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;and everything under it&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;...]
running build_scripts
&lt;span class="o"&gt;[&lt;/span&gt;...]
installing to build/bdist.linux-armv7l/wheel
running &lt;span class="nb"&gt;install
&lt;/span&gt;running install_lib
&lt;span class="o"&gt;[&lt;/span&gt;...]
Copying termtosvg.egg-info to build/bdist.linux-armv7l/wheel/termtosvg-1.0.0-py3.7.egg-info
&lt;span class="o"&gt;[&lt;/span&gt;...]
adding &lt;span class="s1"&gt;'termtosvg-1.0.0.dist-info/RECORD'&lt;/span&gt;
removing build/bdist.linux-armv7l/wheel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Happy? Not really…&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;poddingue@orangepizero:~/termtosvg&lt;span class="nv"&gt;$ &lt;/span&gt;pip3 &lt;span class="nb"&gt;install &lt;/span&gt;dist/termtosvg-1.0.0-py3-none-any.whl
Processing ./dist/termtosvg-1.0.0-py3-none-any.whl
Collecting pyte &lt;span class="o"&gt;(&lt;/span&gt;from &lt;span class="nv"&gt;termtosvg&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;1.0.0&lt;span class="o"&gt;)&lt;/span&gt;
  Downloading https://files.pythonhosted.org/packages/66/37/6fed89b484c8012a0343117f085c92df8447a18af4966d25599861cd5aa0/pyte-0.8.0.tar.gz &lt;span class="o"&gt;(&lt;/span&gt;50kB&lt;span class="o"&gt;)&lt;/span&gt;
    100% |████████████████████████████████| 51kB 768kB/s
Collecting lxml &lt;span class="o"&gt;(&lt;/span&gt;from &lt;span class="nv"&gt;termtosvg&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;1.0.0&lt;span class="o"&gt;)&lt;/span&gt;
  Downloading https://files.pythonhosted.org/packages/e4/19/8dfeef50623892577dc05245093e090bb2bab4c8aed5cad5b03208959563/lxml-4.4.2.tar.gz &lt;span class="o"&gt;(&lt;/span&gt;2.6MB&lt;span class="o"&gt;)&lt;/span&gt;
    100% |████████████████████████████████| 2.6MB 74kB/s
Collecting wcwidth &lt;span class="o"&gt;(&lt;/span&gt;from &lt;span class="nv"&gt;termtosvg&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;1.0.0&lt;span class="o"&gt;)&lt;/span&gt;
  Downloading https://files.pythonhosted.org/packages/58/b4/4850a0ccc6f567cc0ebe7060d20ffd4258b8210efadc259da62dc6ed9c65/wcwidth-0.1.8-py2.py3-none-any.whl
Building wheels &lt;span class="k"&gt;for &lt;/span&gt;collected packages: pyte, lxml
  Running setup.py bdist_wheel &lt;span class="k"&gt;for &lt;/span&gt;pyte ... &lt;span class="k"&gt;done
  &lt;/span&gt;Stored &lt;span class="k"&gt;in &lt;/span&gt;directory: /home/poddingue/.cache/pip/wheels/c0/dd/4a/d0ec26b9d07a3b48e25ba3456dc9bcab875686af6da9e23fcd
  Running setup.py bdist_wheel &lt;span class="k"&gt;for &lt;/span&gt;lxml ... error
  Complete output from &lt;span class="nb"&gt;command&lt;/span&gt; /usr/bin/python3 &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"import setuptools, tokenize;__file__='/tmp/pip-install-4whala87/lxml/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('&lt;/span&gt;&lt;span class="se"&gt;\r\n&lt;/span&gt;&lt;span class="s2"&gt;', '&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;');f.close();exec(compile(code, __file__, 'exec'))"&lt;/span&gt; bdist_wheel &lt;span class="nt"&gt;-d&lt;/span&gt; /tmp/pip-wheel-w27wrikj &lt;span class="nt"&gt;--python-tag&lt;/span&gt; cp37:
  Building lxml version 4.4.2.
  Building without Cython.
  ERROR: b&lt;span class="s1"&gt;'/bin/sh: 1: xslt-config: not found\n'&lt;/span&gt;
  _ make sure the development packages of libxml2 and libxslt are installed _

  Using build configuration of libxslt
  running bdist_wheel
  running build
  running build_py
  &lt;span class="o"&gt;[&lt;/span&gt;...]
  arm-linux-gnueabihf-gcc &lt;span class="nt"&gt;-pthread&lt;/span&gt; &lt;span class="nt"&gt;-DNDEBUG&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; &lt;span class="nt"&gt;-fwrapv&lt;/span&gt; &lt;span class="nt"&gt;-O2&lt;/span&gt; &lt;span class="nt"&gt;-Wall&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; &lt;span class="nt"&gt;-fstack-protector-strong&lt;/span&gt; &lt;span class="nt"&gt;-Wformat&lt;/span&gt; &lt;span class="nt"&gt;-Werror&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;format-security &lt;span class="nt"&gt;-Wdate-time&lt;/span&gt; &lt;span class="nt"&gt;-D_FORTIFY_SOURCE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nt"&gt;-fPIC&lt;/span&gt; &lt;span class="nt"&gt;-DCYTHON_CLINE_IN_TRACEBACK&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nt"&gt;-Isrc&lt;/span&gt; &lt;span class="nt"&gt;-Isrc&lt;/span&gt;/lxml/includes &lt;span class="nt"&gt;-I&lt;/span&gt;/usr/include/python3.7m &lt;span class="nt"&gt;-c&lt;/span&gt; src/lxml/etree.c &lt;span class="nt"&gt;-o&lt;/span&gt; build/temp.linux-armv7l-3.7/src/lxml/etree.o &lt;span class="nt"&gt;-w&lt;/span&gt;
  In file included from src/lxml/etree.c:692:
  src/lxml/includes/etree_defs.h:14:10: fatal error: libxml/xmlversion.h: No such file or directory
   &lt;span class="c"&gt;#include "libxml/xmlversion.h"&lt;/span&gt;
            ^~~~~~~~~~~~~~~~~~~~~
  compilation terminated.
  Compile failed: &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="s1"&gt;'arm-linux-gnueabihf-gcc'&lt;/span&gt; failed with &lt;span class="nb"&gt;exit &lt;/span&gt;status 1
  creating tmp
  cc &lt;span class="nt"&gt;-I&lt;/span&gt;/usr/include/libxml2 &lt;span class="nt"&gt;-c&lt;/span&gt; /tmp/xmlXPathInitp1a2wq_x.c &lt;span class="nt"&gt;-o&lt;/span&gt; tmp/xmlXPathInitp1a2wq_x.o
  /tmp/xmlXPathInitp1a2wq_x.c:1:10: fatal error: libxml/xpath.h: No such file or directory
   &lt;span class="c"&gt;#include "libxml/xpath.h"&lt;/span&gt;
            ^~~~~~~~~~~~~~~~
  compilation terminated.
  &lt;span class="k"&gt;*********************************************************************************&lt;/span&gt;
  Could not find &lt;span class="k"&gt;function &lt;/span&gt;xmlCheckVersion &lt;span class="k"&gt;in &lt;/span&gt;library libxml2. Is libxml2 installed?
  &lt;span class="k"&gt;*********************************************************************************&lt;/span&gt;
  error: &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="s1"&gt;'arm-linux-gnueabihf-gcc'&lt;/span&gt; failed with &lt;span class="nb"&gt;exit &lt;/span&gt;status 1
  &lt;span class="o"&gt;[&lt;/span&gt;...]
Command &lt;span class="s2"&gt;"/usr/bin/python3 -u -c "&lt;/span&gt;import setuptools, tokenize&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;__file__&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'/tmp/pip-install-4whala87/lxml/setup.py'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;getattr&lt;span class="o"&gt;(&lt;/span&gt;tokenize, &lt;span class="s1"&gt;'open'&lt;/span&gt;, open&lt;span class="o"&gt;)(&lt;/span&gt;__file__&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;f.read&lt;span class="o"&gt;()&lt;/span&gt;.replace&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'\r\n'&lt;/span&gt;, &lt;span class="s1"&gt;'\n'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;f.close&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;compile&lt;span class="o"&gt;(&lt;/span&gt;code, __file__, &lt;span class="s1"&gt;'exec'&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;" install --record /tmp/pip-record-5dypr5tk/install-record.txt --single-version-externally-managed --compile --user --prefix="&lt;/span&gt; failed with error code 1 &lt;span class="k"&gt;in&lt;/span&gt; /tmp/pip-install-4whala87/lxml/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Damn!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;poddingue@orangepizero:~/termtosvg&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;libxml2-dev libxslt-dev python3-dev
Reading package lists... Done
Building dependency tree
Reading state information... Done
Note, selecting &lt;span class="s1"&gt;'libxslt1-dev'&lt;/span&gt; instead of &lt;span class="s1"&gt;'libxslt-dev'&lt;/span&gt;
python3-dev is already the newest version &lt;span class="o"&gt;(&lt;/span&gt;3.7.3-1&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
The following additional packages will be installed:
  icu-devtools libicu-dev libxslt1.1
Suggested packages:
  icu-doc
The following NEW packages will be installed:
  icu-devtools libicu-dev libxml2-dev libxslt1-dev libxslt1.1
0 upgraded, 5 newly installed, 0 to remove and 0 not upgraded.
Need to get 10.5 MB of archives.
After this operation, 44.6 MB of additional disk space will be used.
Do you want to &lt;span class="k"&gt;continue&lt;/span&gt;? &lt;span class="o"&gt;[&lt;/span&gt;Y/n]
Get:1 http://httpredir.debian.org/debian buster/main armhf icu-devtools armhf 63.1-6 &lt;span class="o"&gt;[&lt;/span&gt;168 kB]
Get:2 http://httpredir.debian.org/debian buster/main armhf libicu-dev armhf 63.1-6 &lt;span class="o"&gt;[&lt;/span&gt;8,934 kB]
Get:3 http://httpredir.debian.org/debian buster/main armhf libxml2-dev armhf 2.9.4+dfsg1-7+b3 &lt;span class="o"&gt;[&lt;/span&gt;718 kB]
Get:4 http://httpredir.debian.org/debian buster/main armhf libxslt1.1 armhf 1.1.32-2.2~deb10u1 &lt;span class="o"&gt;[&lt;/span&gt;217 kB]
Get:5 http://httpredir.debian.org/debian buster/main armhf libxslt1-dev armhf 1.1.32-2.2~deb10u1 &lt;span class="o"&gt;[&lt;/span&gt;507 kB]
Fetched 10.5 MB &lt;span class="k"&gt;in &lt;/span&gt;5s &lt;span class="o"&gt;(&lt;/span&gt;2,318 kB/s&lt;span class="o"&gt;)&lt;/span&gt;
Selecting previously unselected package icu-devtools.
&lt;span class="o"&gt;(&lt;/span&gt;Reading database ... 66802 files and directories currently installed.&lt;span class="o"&gt;)&lt;/span&gt;
Preparing to unpack .../icu-devtools_63.1-6_armhf.deb ...
Unpacking icu-devtools &lt;span class="o"&gt;(&lt;/span&gt;63.1-6&lt;span class="o"&gt;)&lt;/span&gt; ...
Selecting previously unselected package libicu-dev:armhf.
Preparing to unpack .../libicu-dev_63.1-6_armhf.deb ...
Unpacking libicu-dev:armhf &lt;span class="o"&gt;(&lt;/span&gt;63.1-6&lt;span class="o"&gt;)&lt;/span&gt; ...
Selecting previously unselected package libxml2-dev:armhf.
Preparing to unpack .../libxml2-dev_2.9.4+dfsg1-7+b3_armhf.deb ...
Unpacking libxml2-dev:armhf &lt;span class="o"&gt;(&lt;/span&gt;2.9.4+dfsg1-7+b3&lt;span class="o"&gt;)&lt;/span&gt; ...
Selecting previously unselected package libxslt1.1:armhf.
Preparing to unpack .../libxslt1.1_1.1.32-2.2~deb10u1_armhf.deb ...
Unpacking libxslt1.1:armhf &lt;span class="o"&gt;(&lt;/span&gt;1.1.32-2.2~deb10u1&lt;span class="o"&gt;)&lt;/span&gt; ...
Selecting previously unselected package libxslt1-dev:armhf.
Preparing to unpack .../libxslt1-dev_1.1.32-2.2~deb10u1_armhf.deb ...
Unpacking libxslt1-dev:armhf &lt;span class="o"&gt;(&lt;/span&gt;1.1.32-2.2~deb10u1&lt;span class="o"&gt;)&lt;/span&gt; ...
Setting up icu-devtools &lt;span class="o"&gt;(&lt;/span&gt;63.1-6&lt;span class="o"&gt;)&lt;/span&gt; ...
Setting up libxslt1.1:armhf &lt;span class="o"&gt;(&lt;/span&gt;1.1.32-2.2~deb10u1&lt;span class="o"&gt;)&lt;/span&gt; ...
Setting up libicu-dev:armhf &lt;span class="o"&gt;(&lt;/span&gt;63.1-6&lt;span class="o"&gt;)&lt;/span&gt; ...
Setting up libxml2-dev:armhf &lt;span class="o"&gt;(&lt;/span&gt;2.9.4+dfsg1-7+b3&lt;span class="o"&gt;)&lt;/span&gt; ...
Setting up libxslt1-dev:armhf &lt;span class="o"&gt;(&lt;/span&gt;1.1.32-2.2~deb10u1&lt;span class="o"&gt;)&lt;/span&gt; ...
Processing triggers &lt;span class="k"&gt;for &lt;/span&gt;man-db &lt;span class="o"&gt;(&lt;/span&gt;2.8.5-2&lt;span class="o"&gt;)&lt;/span&gt; ...
Processing triggers &lt;span class="k"&gt;for &lt;/span&gt;libc-bin &lt;span class="o"&gt;(&lt;/span&gt;2.28-10&lt;span class="o"&gt;)&lt;/span&gt; ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All set? Not so sure…&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Command &lt;span class="s2"&gt;"/usr/bin/python3 -u -c "&lt;/span&gt;import setuptools, tokenize&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;__file__&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'/tmp/pip-install-9lgddbtt/lxml/setup.py'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;getattr&lt;span class="o"&gt;(&lt;/span&gt;tokenize, &lt;span class="s1"&gt;'open'&lt;/span&gt;, open&lt;span class="o"&gt;)(&lt;/span&gt;__file__&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nv"&gt;code&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;f.read&lt;span class="o"&gt;()&lt;/span&gt;.replace&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'\r\n'&lt;/span&gt;, &lt;span class="s1"&gt;'\n'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;f.close&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;compile&lt;span class="o"&gt;(&lt;/span&gt;code, __file__, &lt;span class="s1"&gt;'exec'&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="s2"&gt;" install --record /tmp/pip-record-ghyj96g8/install-record.txt --single-version-externally-managed --compile --user --prefix="&lt;/span&gt; failed with error code 1 &lt;span class="k"&gt;in&lt;/span&gt; /tmp/pip-install-9lgddbtt/lxml/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The error may have happened because of low memory. Let’s this machine breathe a little better by giving it more swap:&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="nb"&gt;sudo &lt;/span&gt;fallocate &lt;span class="nt"&gt;-l&lt;/span&gt; 1G /swapfile
&lt;span class="nb"&gt;sudo chmod &lt;/span&gt;600 /swapfile
&lt;span class="nb"&gt;sudo &lt;/span&gt;mkswap /swapfile
&lt;span class="nb"&gt;sudo &lt;/span&gt;swapon /swapfile
&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/fstab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let’s add this line in &lt;code&gt;/etc/fstab&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/swapfile swap swap defaults 0 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s try to install it once again. Go and have a coffee, or go for a walk…​ You could maybe even take time to marry and start a family before the build finishes. Anyway, after some time, you’ll have your beautiful termtosvg package ready to rock!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip3 &lt;span class="nb"&gt;install &lt;/span&gt;dist/termtosvg-1.0.0-py3-none-any.whl
Processing ./dist/termtosvg-1.0.0-py3-none-any.whl
Collecting lxml &lt;span class="o"&gt;(&lt;/span&gt;from &lt;span class="nv"&gt;termtosvg&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;1.0.0&lt;span class="o"&gt;)&lt;/span&gt;
  Using cached https://files.pythonhosted.org/packages/e4/19/8dfeef50623892577dc05245093e090bb2bab4c8aed5cad5b03208959563/lxml
Requirement already satisfied: pyte &lt;span class="k"&gt;in&lt;/span&gt; /home/poddingue/.local/lib/python3.7/site-packages &lt;span class="o"&gt;(&lt;/span&gt;from &lt;span class="nv"&gt;termtosvg&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;1.0.0&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;0.8.0&lt;span class="o"&gt;)&lt;/span&gt;
Requirement already satisfied: wcwidth &lt;span class="k"&gt;in&lt;/span&gt; /home/poddingue/.local/lib/python3.7/site-packages &lt;span class="o"&gt;(&lt;/span&gt;from &lt;span class="nv"&gt;termtosvg&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;1.0.0&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;0.1.8&lt;span class="o"&gt;)&lt;/span&gt;
Building wheels &lt;span class="k"&gt;for &lt;/span&gt;collected packages: lxml
  Running setup.py bdist_wheel &lt;span class="k"&gt;for &lt;/span&gt;lxml ... -


&lt;span class="k"&gt;done
  &lt;/span&gt;Stored &lt;span class="k"&gt;in &lt;/span&gt;directory: /home/poddingue/.cache/pip/wheels/73/52/7c/5cd696851d3e5e31a05023dd402b04659a6ec695ecc566c9d3
Successfully built lxml
Installing collected packages: lxml, termtosvg
Successfully installed lxml-4.4.2 termtosvg-1.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time, it looks like our installation has been successful. Let’s try it for real now.&lt;/p&gt;

&lt;h1&gt;
  
  
  Testing
&lt;/h1&gt;

&lt;p&gt;Just enter termtosvg, do your stuff, and then do exit. You will have a message that will tell you where your newly created file lies.&lt;/p&gt;

&lt;p&gt;That’s cool, but can we do any better? The man &lt;a href="https://github.com/nbedos/termtosvg/blob/develop/man/termtosvg.md" rel="noopener noreferrer"&gt;page&lt;/a&gt; tells us we can record a terminal session with a specific screen geometry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;termtosvg &lt;span class="nt"&gt;-g&lt;/span&gt; 80x24 animation.svg
Recording started, enter &lt;span class="s2"&gt;"exit"&lt;/span&gt; &lt;span class="nb"&gt;command &lt;/span&gt;or Control-D to end
poddingue@orangepizero:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Hello, World!"&lt;/span&gt;
Hello, World!
poddingue@orangepizero:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;exit
&lt;/span&gt;Rendering ended, SVG animation is animation.svg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pretty handy!&lt;/p&gt;

&lt;p&gt;I am recording my casts with a 200x60 geometry, exporting them in the SVG format. The first step is to record them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;termtosvg record &lt;span class="nt"&gt;-g&lt;/span&gt; 200x60 whatever-name-I-choose.cast
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I then ask termtosvg to render the cast as SVG, so that I can use them in websites that can handle them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;termtosvg render whatever-name-I-choose.cast whatever-name-I-choose.svg
Rendering started
Rendering ended, SVG animation is whatever-name-I-choose.svg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Want more? You’re GIF hungry? Ok I resign myself to give you my process, which is not pretty, and requires an X86 machine. As we have seen before, we can record a terminal session in asciicast v2 format. The cast file we have produced during the previous step to generate SVG, can then be used by &lt;a href="https://github.com/asciinema/asciicast2gif?source=post_page---------------------------" rel="noopener noreferrer"&gt;asciinema&lt;/a&gt; (using &lt;a href="https://phantomjs.org/" rel="noopener noreferrer"&gt;phantomjs&lt;/a&gt; in the background) to produce a GIF file.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker pull asciinema/asciicast2gif
alias asciicast2gif='docker run --rm -v $PWD:/data asciinema/asciicast2gif'
asciicast2gif -s 2 -t solarized-dark whatever-name-I-choose.cast demo.gif
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And here is the final result:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2Fwhatever-name-I-choose.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2Fwhatever-name-I-choose.svg" alt="whatever-name-I-choose" width="1600" height="1022.0"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And in GIF for the CMS that can’t display properly SVG.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2Fwhatever-name-I-choose.cast.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2Fwhatever-name-I-choose.cast.gif" alt="whatever-name-I-choose.cast" width="2912" height="1944"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>armbian</category>
      <category>h3</category>
      <category>termtosvg</category>
    </item>
    <item>
      <title>Sphinx: Installing sphinxbase on the OrangePi 4B</title>
      <dc:creator>Bruno Verachten</dc:creator>
      <pubDate>Wed, 22 Apr 2026 07:44:21 +0000</pubDate>
      <link>https://dev.to/gounthar/sphinx-installing-sphinxbase-on-the-orangepi-4b-223e</link>
      <guid>https://dev.to/gounthar/sphinx-installing-sphinxbase-on-the-orangepi-4b-223e</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on June 5, 2020 at &lt;a href="https://bruno.verachten.fr/2020/06/05/installing-sphinxbase-on-the-orangepi-4b/" rel="noopener noreferrer"&gt;https://bruno.verachten.fr/2020/06/05/installing-sphinxbase-on-the-orangepi-4b/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;This post will be part of a series regarding generating subtitles for videos with the &lt;a href="http://www.orangepi.org/Orange%20Pi%204B/" rel="noopener noreferrer"&gt;OrangePi 4B&lt;/a&gt; running &lt;a href="https://www.armbian.com/orange-pi-4/" rel="noopener noreferrer"&gt;Armbian&lt;/a&gt;. We’re not yet sure of what we’ll use to generate those subtitles, so for the time being, we’ll use &lt;a href="https://cmusphinx.github.io/wiki/tutorialadapt/" rel="noopener noreferrer"&gt;Sphinx&lt;/a&gt;, and in the following months, we’ll try to setup something that will take advantage of the &lt;a href="https://drive.google.com/drive/folders/1QMU5n-7fCx28cQPH_lu2L0wrFWeC3G45" rel="noopener noreferrer"&gt;NPU&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Today, we’ll install SphinxBase.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/cmusphinx/sphinxbase.git
&lt;span class="nb"&gt;cd &lt;/span&gt;sphinxbase/
./autogen.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unfortunately we get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;_Error_: You must have &lt;span class="sb"&gt;`&lt;/span&gt;libtool&lt;span class="s1"&gt;' installed.
Get ftp://ftp.gnu.org/pub/gnu/libtool/libtool-2.2.6b.tar.gz
(or a newer version if it is available)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, my bad:&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="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;libtool
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s give it another try:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./autogen.sh
&lt;span class="o"&gt;[&lt;/span&gt;...]
configure: error: &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;/home/poddingue/sphinxbase&lt;span class="s1"&gt;':
configure: error:
  Could not link test program to Python. Maybe the main Python library has been
  installed in some non-standard library path. If so, pass it to configure,
  via the LDFLAGS environment variable.
  Example: ./configure LDFLAGS="-L/usr/non-standard-path/python/lib"
  ============================================================================
   ERROR!
   You probably have to install the development version of the Python package
   for your distribution.  The exact name of this package varies among them.
  ============================================================================

See `config.log'&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;more details
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I see. I may have forgotten to install Python-dev…​&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="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;python3-dev
&lt;span class="o"&gt;[&lt;/span&gt;...]
./autogen.sh
&lt;span class="o"&gt;[&lt;/span&gt;...]
configure: error: &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;/home/poddingue/sphinxbase&lt;span class="s1"&gt;':
configure: error:
  Could not link test program to Python. Maybe the main Python library has been
  installed in some non-standard library path. If so, pass it to configure,
  via the LDFLAGS environment variable.
  Example: ./configure LDFLAGS="-L/usr/non-standard-path/python/lib"
  ============================================================================
   ERROR!
   You probably have to install the development version of the Python package
   for your distribution.  The exact name of this package varies among them.
  ============================================================================
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How come? Are you still working with Python 2.x? Naaaah…​ Ok, you never know, let’s try it this way:&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="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;python-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Yes, that was it. Now, let’s tackle this error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;checking python extra linking flags... &lt;span class="nt"&gt;-Xlinker&lt;/span&gt; &lt;span class="nt"&gt;-export-dynamic&lt;/span&gt; &lt;span class="nt"&gt;-Wl&lt;/span&gt;,-O1 &lt;span class="nt"&gt;-Wl&lt;/span&gt;,-Bsymbolic-functions
checking consistency of all components of python development environment... &lt;span class="nb"&gt;yes
&lt;/span&gt;checking &lt;span class="k"&gt;for &lt;/span&gt;swig... no
checking &lt;span class="k"&gt;for &lt;/span&gt;swig2.0... no
configure: error: swig not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should be solved by&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="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;swig
./autogen.sh
&lt;span class="o"&gt;[&lt;/span&gt;...]
config.status: executing libtool commands
Now &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;make&lt;span class="s1"&gt;' to compile the package.
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Bingo! Time for a good old&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make &lt;span class="nt"&gt;-j6&lt;/span&gt;
make check
&lt;span class="o"&gt;[&lt;/span&gt;...]
Testsuite summary &lt;span class="k"&gt;for &lt;/span&gt;sphinxbase 5prealpha
&lt;span class="o"&gt;============================================================================&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt; TOTAL: 10
&lt;span class="o"&gt;==&lt;/span&gt; PASS:  10
&lt;span class="o"&gt;==&lt;/span&gt; SKIP:  0
&lt;span class="o"&gt;==&lt;/span&gt; XFAIL: 0
&lt;span class="o"&gt;==&lt;/span&gt; FAIL:  0
&lt;span class="o"&gt;==&lt;/span&gt; XPASS: 0
&lt;span class="o"&gt;==&lt;/span&gt; ERROR: 0
&lt;span class="o"&gt;============================================================================&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All went fine. Let’s install it then:&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="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Done. Next step, install Pocket Sphinx.&lt;/p&gt;

</description>
      <category>armbian</category>
      <category>machinelearning</category>
      <category>merrykombo</category>
      <category>rk3399</category>
    </item>
    <item>
      <title>Installing TensorFlow on the Orange Pi 4B</title>
      <dc:creator>Bruno Verachten</dc:creator>
      <pubDate>Wed, 22 Apr 2026 07:44:19 +0000</pubDate>
      <link>https://dev.to/gounthar/installing-tensorflow-on-the-orange-pi-4b-e60</link>
      <guid>https://dev.to/gounthar/installing-tensorflow-on-the-orange-pi-4b-e60</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on June 19, 2020 at &lt;a href="https://bruno.verachten.fr/2020/06/19/installing-tensorflow-on-the-orangepi-4b/" rel="noopener noreferrer"&gt;https://bruno.verachten.fr/2020/06/19/installing-tensorflow-on-the-orangepi-4b/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;This quick howto is heavily based on Orange Pi’s &lt;a href="http://www.orangepi.org/downloadresources/OrangePi4B/2020-01-03/orangepi3_46708b05ab3339f316305414ee.html" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;. To this day, &lt;a href="http://www.orangepi.org/" rel="noopener noreferrer"&gt;Orange Pi&lt;/a&gt; recommended versions of &lt;a href="https://www.tensorflow.org/" rel="noopener noreferrer"&gt;TensorFlow&lt;/a&gt; and &lt;a href="https://www.python.org/downloads/" rel="noopener noreferrer"&gt;Python&lt;/a&gt; are quite old:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;TensorFlow&lt;/em&gt;：1.14.0 &lt;em&gt;Python environment&lt;/em&gt;：python2.7&lt;/p&gt;

&lt;h1&gt;
  
  
  Installation of Python environment and dependencies
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;python-pip python-dev libatlas-base-dev
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; password &lt;span class="k"&gt;for &lt;/span&gt;poddingue:
Reading package lists... Done
Building dependency tree
Reading state information... Done
python-dev is already the newest version &lt;span class="o"&gt;(&lt;/span&gt;2.7.16-1&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
The following additional packages will be installed:
  libatlas3-base python-pip-whl
Suggested packages:
  libatlas-doc liblapack-doc
Recommended packages:
  python-all-dev python-setuptools python-wheel
The following NEW packages will be installed:
  libatlas-base-dev libatlas3-base python-pip python-pip-whl
Need to get 7,168 kB of archives.
After this operation, 40.1 MB of additional disk space will be used.
Do you want to &lt;span class="k"&gt;continue&lt;/span&gt;? &lt;span class="o"&gt;[&lt;/span&gt;Y/n]
Get:1 http://debian.proxad.net/debian buster/main arm64 libatlas3-base arm64 3.10.3-8 &lt;span class="o"&gt;[&lt;/span&gt;2,409 kB]
Get:2 http://debian.proxad.net/debian buster/main arm64 libatlas-base-dev arm64 3.10.3-8 &lt;span class="o"&gt;[&lt;/span&gt;2,954 kB]
Get:3 http://debian.proxad.net/debian buster/main arm64 python-pip-whl all 18.1-5 &lt;span class="o"&gt;[&lt;/span&gt;1,591 kB]
Get:4 http://debian.proxad.net/debian buster/main arm64 python-pip all 18.1-5 &lt;span class="o"&gt;[&lt;/span&gt;215 kB]
Fetched 7,168 kB &lt;span class="k"&gt;in &lt;/span&gt;1s &lt;span class="o"&gt;(&lt;/span&gt;8,178 kB/s&lt;span class="o"&gt;)&lt;/span&gt;
Selecting previously unselected package libatlas3-base:arm64.
&lt;span class="o"&gt;(&lt;/span&gt;Reading database ... 112671 files and directories currently installed.&lt;span class="o"&gt;)&lt;/span&gt;
Preparing to unpack .../libatlas3-base_3.10.3-8_arm64.deb ...
Unpacking libatlas3-base:arm64 &lt;span class="o"&gt;(&lt;/span&gt;3.10.3-8&lt;span class="o"&gt;)&lt;/span&gt; ...
Selecting previously unselected package libatlas-base-dev:arm64.
Preparing to unpack .../libatlas-base-dev_3.10.3-8_arm64.deb ...
Unpacking libatlas-base-dev:arm64 &lt;span class="o"&gt;(&lt;/span&gt;3.10.3-8&lt;span class="o"&gt;)&lt;/span&gt; ...
Selecting previously unselected package python-pip-whl.
Preparing to unpack .../python-pip-whl_18.1-5_all.deb ...
Unpacking python-pip-whl &lt;span class="o"&gt;(&lt;/span&gt;18.1-5&lt;span class="o"&gt;)&lt;/span&gt; ...
Selecting previously unselected package python-pip.
Preparing to unpack .../python-pip_18.1-5_all.deb ...
Unpacking python-pip &lt;span class="o"&gt;(&lt;/span&gt;18.1-5&lt;span class="o"&gt;)&lt;/span&gt; ...
Setting up libatlas3-base:arm64 &lt;span class="o"&gt;(&lt;/span&gt;3.10.3-8&lt;span class="o"&gt;)&lt;/span&gt; ...
update-alternatives: using /usr/lib/aarch64-linux-gnu/atlas/libblas.so.3 to provide /usr/lib/aarch64-linux-gnu/libblas.so.3 &lt;span class="o"&gt;(&lt;/span&gt;libblas.so.3-aarch64-linux-gnu&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;auto mode
update-alternatives: using /usr/lib/aarch64-linux-gnu/atlas/liblapack.so.3 to provide /usr/lib/aarch64-linux-gnu/liblapack.so.3 &lt;span class="o"&gt;(&lt;/span&gt;liblapack.so.3-aarch64-linux-gnu&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;auto mode
Setting up libatlas-base-dev:arm64 &lt;span class="o"&gt;(&lt;/span&gt;3.10.3-8&lt;span class="o"&gt;)&lt;/span&gt; ...
update-alternatives: using /usr/lib/aarch64-linux-gnu/atlas/libblas.so to provide /usr/lib/aarch64-linux-gnu/libblas.so &lt;span class="o"&gt;(&lt;/span&gt;libblas.so-aarch64-linux-gnu&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;auto mode
update-alternatives: using /usr/lib/aarch64-linux-gnu/atlas/liblapack.so to provide /usr/lib/aarch64-linux-gnu/liblapack.so &lt;span class="o"&gt;(&lt;/span&gt;liblapack.so-aarch64-linux-gnu&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in &lt;/span&gt;auto mode
Setting up python-pip-whl &lt;span class="o"&gt;(&lt;/span&gt;18.1-5&lt;span class="o"&gt;)&lt;/span&gt; ...
Setting up python-pip &lt;span class="o"&gt;(&lt;/span&gt;18.1-5&lt;span class="o"&gt;)&lt;/span&gt; ...
Processing triggers &lt;span class="k"&gt;for &lt;/span&gt;man-db &lt;span class="o"&gt;(&lt;/span&gt;2.8.5-2&lt;span class="o"&gt;)&lt;/span&gt; ...
Processing triggers &lt;span class="k"&gt;for &lt;/span&gt;libc-bin &lt;span class="o"&gt;(&lt;/span&gt;2.28-10&lt;span class="o"&gt;)&lt;/span&gt; ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Install TensorFlow
&lt;/h1&gt;

&lt;p&gt;There are several ways to install TensorFlow, but here we will install if from a downloaded binary. Download the PIP installation package for TensorFlow from the following page: &lt;a href="https://github.com/lhelontra/tensorflow-on-arm/releases/tag/v1.14.0" rel="noopener noreferrer"&gt;https://github.com/lhelontra/tensorflow-on-arm/releases/tag/v1.14.0&lt;/a&gt; .&lt;/p&gt;

&lt;p&gt;This should give something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://github.com/lhelontra/tensorflow-on-arm/releases/download/v1.14.0/tensorflow-1.14.0-cp27-none-linux_aarch64.whl"&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; tensorflow-1.14.0-cp27-none-linux_aarch64.whl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let’s use PIP to install TensorFlow.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;tensorflow-1.14.0-cp27-none-linux_aarch64.whl
Processing ./tensorflow-1.14.0-cp27-none-linux_aarch64.whl
Collecting keras-preprocessing&amp;gt;&lt;span class="o"&gt;=&lt;/span&gt;1.0.5 &lt;span class="o"&gt;(&lt;/span&gt;from &lt;span class="nv"&gt;tensorflow&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;1.14.0&lt;span class="o"&gt;)&lt;/span&gt;
  Downloading https://files.pythonhosted.org/packages/79/4c/7c3275a01e12ef9368a892926ab932b33bb13d55794881e3573482b378a7/Keras_Preprocessing-1.1.2-py2.py3-none-any.whl &lt;span class="o"&gt;(&lt;/span&gt;42kB&lt;span class="o"&gt;)&lt;/span&gt;
    100% |████████████████████████████████| 51kB 2.5MB/s
Collecting enum34&amp;gt;&lt;span class="o"&gt;=&lt;/span&gt;1.1.6 &lt;span class="o"&gt;(&lt;/span&gt;from &lt;span class="nv"&gt;tensorflow&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;1.14.0&lt;span class="o"&gt;)&lt;/span&gt;
  Downloading https://files.pythonhosted.org/packages/6f/2c/a9386903ece2ea85e9807e0e062174dc26fdce8b05f216d00491be29fad5/enum34-1.1.10-py2-none-any.whl
Collecting wheel &lt;span class="o"&gt;(&lt;/span&gt;from &lt;span class="nv"&gt;tensorflow&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;1.14.0&lt;span class="o"&gt;)&lt;/span&gt;
  Downloading https://files.pythonhosted.org/packages/8c/23/848298cccf8e40f5bbb59009b32848a4c38f4e7f3364297ab3c3e2e2cd14/wheel-0.34.2-py2.py3-none-any.whl
Collecting wrapt&amp;gt;&lt;span class="o"&gt;=&lt;/span&gt;1.11.1 &lt;span class="o"&gt;(&lt;/span&gt;from &lt;span class="nv"&gt;tensorflow&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;1.14.0&lt;span class="o"&gt;)&lt;/span&gt;
  Downloading https://files.pythonhosted.org/packages/82/f7/e43cefbe88c5fd371f4cf0cf5eb3feccd07515af9fd6cf7dbf1d1793a797/wrapt-1.12.1.tar.gz
    Complete output from &lt;span class="nb"&gt;command &lt;/span&gt;python setup.py egg_info:
    Traceback &lt;span class="o"&gt;(&lt;/span&gt;most recent call last&lt;span class="o"&gt;)&lt;/span&gt;:
      File &lt;span class="s2"&gt;"&amp;lt;string&amp;gt;"&lt;/span&gt;, line 1, &lt;span class="k"&gt;in&lt;/span&gt; &amp;lt;module&amp;gt;
    ImportError: No module named setuptools
&lt;span class="nt"&gt;----------------------------------------&lt;/span&gt;
Command &lt;span class="s2"&gt;"python setup.py egg_info"&lt;/span&gt; failed with error code 1 &lt;span class="k"&gt;in&lt;/span&gt; /tmp/pip-install-Dd0045/wrapt/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Of course…​&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="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; python-setuptools
&lt;span class="o"&gt;[&lt;/span&gt;...]
  error: invalid &lt;span class="nb"&gt;command&lt;/span&gt; &lt;span class="s1"&gt;'bdist_wheel'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Damn!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;wheel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And&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="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;:/home/poddingue/.local/bin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;in `~/.bashrc`, because of
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;wheel
Collecting wheel
  Using cached https://files.pythonhosted.org/packages/8c/23/848298cccf8e40f5bbb59009b32848a4c38f4e7f3364297ab3c3e2e2cd14/wheel-0.34.2-py2.py3-none-any.whl
Installing collected packages: wheel
  The script wheel is installed &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s1"&gt;'/home/poddingue/.local/bin'&lt;/span&gt; which is not on PATH.
  Consider adding this directory to PATH or, &lt;span class="k"&gt;if &lt;/span&gt;you prefer to suppress this warning, use &lt;span class="nt"&gt;--no-warn-script-location&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Successfully installed wheel-0.34.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s issue once more `&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;` bash&lt;br&gt;
pip install tensorflow-1.14.0-cp27-none-linux_aarch64.whl&lt;/p&gt;

&lt;p&gt;​    error: libhdf5.so: cannot open shared object file: No such file or directory&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Bad luck.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
Command "/usr/bin/python -u -c "import setuptools, tokenize;__file__='/tmp/pip-install-irkqLB/h5py/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /tmp/pip-record-N9oIr_/install-record.txt --single-version-externally-managed --compile --user --prefix=" failed with error code 1 in /tmp/pip-install-irkqLB/h5py/&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Something must be missing…​&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
Command "/usr/bin/python -u -c "import setuptools, tokenize;__file__='/tmp/pip-install-irkqLB/h5py/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /tmp/pip-record-N9oIr_/install-record.txt --single-version-externally-managed --compile --user --prefix=" failed with error code 1 in /tmp/pip-install-irkqLB/h5py/&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Not better…​ I think the machine is trying to tell me something…​ A missing library perhaps?&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
sudo apt install python-h5py&lt;br&gt;
pip install tensorflow-1.14.0-cp27-none-linux_aarch64.whl&lt;br&gt;
[...]&lt;br&gt;
Successfully installed astor-0.8.1 backports.weakref-1.0.post1 funcsigs-1.0.2 gast-0.3.3 google-pasta-0.2.0 keras-applications-1.0.8 mock-3.0.5 tensorflow-1.14.0 tensorflow-estimator-1.14.0 termcolor-1.1.0&lt;br&gt;
rm tensorflow-1.14.0-cp27-none-linux_aarch64.whl&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;It worked!&lt;/p&gt;

&lt;p&gt;Now, let’s edit a test.py file:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;` python&lt;br&gt;
import tensorflow as tf&lt;br&gt;
hello = tf.constant('Hello, TensorFlow!')&lt;/p&gt;

&lt;h1&gt;
  
  
  sess = tf.Session()
&lt;/h1&gt;

&lt;p&gt;sess = tf.compat.v1.Session()&lt;br&gt;
print(sess.run(hello))&lt;br&gt;
`&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
python test.py&lt;br&gt;
Hello, TensorFlow!&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We all have to start somewhere, don’t we?&lt;/p&gt;

&lt;h1&gt;
  
  
  TL;DR
&lt;/h1&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;bash&lt;br&gt;
sudo apt-get install -y python-setuptools python-h5py python-pip python-dev libatlas-base-dev&lt;br&gt;
curl -L "https://github.com/lhelontra/tensorflow-on-arm/releases/download/v1.14.0/tensorflow-1.14.0-cp27-none-linux_aarch64.whl" --output tensorflow-1.14.0-cp27-none-linux_aarch64.whll&lt;br&gt;
pip install wheel&lt;br&gt;
pip install tensorflow-1.14.0-cp27-none-linux_aarch64.whl&lt;br&gt;
cat &amp;gt;&amp;gt; ~/.bashrc&lt;br&gt;
  export PATH=$PATH:/home/poddingue/.local/bin&lt;br&gt;
  CTRL+D&lt;br&gt;
source ~/.bashrc&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

</description>
      <category>4b</category>
      <category>armbian</category>
      <category>machinelearning</category>
      <category>merrykombo</category>
    </item>
    <item>
      <title>Installing Caffe on the Orange Pi 4B</title>
      <dc:creator>Bruno Verachten</dc:creator>
      <pubDate>Wed, 22 Apr 2026 07:44:17 +0000</pubDate>
      <link>https://dev.to/gounthar/installing-caffe-on-the-orange-pi-4b-5a1</link>
      <guid>https://dev.to/gounthar/installing-caffe-on-the-orange-pi-4b-5a1</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on June 22, 2020 at &lt;a href="https://bruno.verachten.fr/2020/06/22/installing-caffee-on-the-orangepi-4b/" rel="noopener noreferrer"&gt;https://bruno.verachten.fr/2020/06/22/installing-caffee-on-the-orangepi-4b/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Install dependencies
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;libprotobuf-dev libleveldb-dev libsnappy-dev
libopencv-dev libhdf5-serial-dev protobuf-compiler cmake
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; libboost-all-dev
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;libopenblas-dev liblapack-dev libatlas-base-dev
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;libgflags-dev libgoogle-glog-dev liblmdb-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Download Caffe and modify configuration parameters
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/BVLC/caffe
&lt;span class="nb"&gt;cd &lt;/span&gt;caffe
&lt;span class="nb"&gt;cp &lt;/span&gt;Makefile.config.example Makefile.config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modify the parameters of Makefile.config Change &lt;code&gt;#CPU_ONLY := 1&lt;/code&gt; into &lt;code&gt;CPU_ONLY := 1&lt;/code&gt; Change &lt;code&gt;＃OPENCV_VERSION := 3&lt;/code&gt; into &lt;code&gt;OPENCV_VERSION := 3&lt;/code&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Install Caffe
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;build
&lt;span class="nb"&gt;cd &lt;/span&gt;build
cmake ..
  Protobuf compiler version doesn&lt;span class="s1"&gt;'t match library version 3.6.1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Okay…​ Let’s try a &lt;code&gt;sudo apt install protobuf-compiler&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;protoc &lt;span class="nt"&gt;--version&lt;/span&gt;
libprotoc 3.6.1
cmake ..
  Could NOT find HDF5 &lt;span class="o"&gt;(&lt;/span&gt;missing: HDF5_LIBRARIES HDF5_INCLUDE_DIRS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My bad, let’s install it.&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="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;libhdf5-dev
cmake ..
  Could not find a package configuration file provided by &lt;span class="s2"&gt;"OpenCV"&lt;/span&gt; with any
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s install openCV, then…​&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="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;libopencv-dev
cmake ..
&lt;span class="o"&gt;[&lt;/span&gt;...]
&lt;span class="nt"&gt;--&lt;/span&gt; Configuring &lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="nt"&gt;--&lt;/span&gt; Generating &lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="nt"&gt;--&lt;/span&gt; Build files have been written to: /home/poddingue/caffe/build
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next step, let’s build it for real. By the way, kiddoes, don’t try this &lt;code&gt;-j6&lt;/code&gt; at home unless you have a good heatsink…​ Or the machine will stop/hang up/reboot without prior alert.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make all &lt;span class="nt"&gt;-j6&lt;/span&gt;
make pycaffe &lt;span class="nt"&gt;-j6&lt;/span&gt;
make &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-j6&lt;/span&gt;
make runtest &lt;span class="nt"&gt;-j6&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Run a simple example
&lt;/h1&gt;

&lt;p&gt;Run the caffe example — mnist instance: Mnist is a handwritten digital library. Originally used for handwritten numeral recognition on checks, and it is now the DL’s starting exercise library. The special model for mnist identification is Lenet, which is the earliest CNN model. The training samples of mnist data are 60,000 units, and the test samples are 10,000 units. Each sample is a black and white picture of size 28*28, and the handwritten number is 0-9, so it is divided into 10 categories.&lt;/p&gt;

&lt;h2&gt;
  
  
  Download the mnist data first
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sh data/mnist/get_mnist.sh
&lt;span class="nb"&gt;cd &lt;/span&gt;data/mnist
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are four files in the directory after running the previous command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ll
total 53676
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt; 1 poddingue poddingue      408 Jun 22 16:55 get_mnist.sh
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue  7840016 Jul 21  2000 t10k-images-idx3-ubyte &lt;span class="c"&gt;# Training set samples&lt;/span&gt;
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue    10008 Jul 21  2000 t10k-labels-idx1-ubyte &lt;span class="c"&gt;# Corresponding annotation of training set&lt;/span&gt;
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue 47040016 Jul 21  2000 train-images-idx3-ubyte &lt;span class="c"&gt;# Testing set sample&lt;/span&gt;
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue    60008 Jul 21  2000 train-labels-idx1-ubyte &lt;span class="c"&gt;# Corresponding annotation of testing set&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Converting the data
&lt;/h2&gt;

&lt;p&gt;These data cannot be used directly in Caffe, and need to be converted into LMDB data:&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="nb"&gt;cd&lt;/span&gt; ../..
sh examples/mnist/create_mnist.sh
Creating lmdb...
I0622 17:37:06.109530 12691 db_lmdb.cpp:35] Opened lmdb examples/mnist/mnist_train_lmdb
I0622 17:37:06.114603 12691 convert_mnist_data.cpp:88] A total of 60000 items.
I0622 17:37:06.114666 12691 convert_mnist_data.cpp:89] Rows: 28 Cols: 28
I0622 17:37:11.407480 12691 convert_mnist_data.cpp:108] Processed 60000 files.
I0622 17:37:12.192065 12698 db_lmdb.cpp:35] Opened lmdb examples/mnist/mnist_test_lmdb
I0622 17:37:12.194784 12698 convert_mnist_data.cpp:88] A total of 10000 items.
I0622 17:37:12.198009 12698 convert_mnist_data.cpp:89] Rows: 28 Cols: 28
I0622 17:37:12.566424 12698 convert_mnist_data.cpp:108] Processed 10000 files.
Done.
ll examples/mnist/
total 136
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue  4520 Jun 22 16:55 convert_mnist_data.cpp
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt; 1 poddingue poddingue   634 Jun 22 16:55 create_mnist.sh
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue   777 Jun 22 16:55 lenet_adadelta_solver.prototxt
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue   778 Jun 22 16:55 lenet_auto_solver.prototxt
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue  6003 Jun 22 16:55 lenet_consolidated_solver.prototxt
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue   871 Jun 22 16:55 lenet_multistep_solver.prototxt
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue  1738 Jun 22 16:55 lenet.prototxt
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue   886 Jun 22 16:55 lenet_solver_adam.prototxt
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue   790 Jun 22 16:55 lenet_solver.prototxt
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue   830 Jun 22 16:55 lenet_solver_rmsprop.prototxt
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue  2282 Jun 22 16:55 lenet_train_test.prototxt
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue  4814 Jun 22 16:55 mnist_autoencoder.prototxt
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue   451 Jun 22 16:55 mnist_autoencoder_solver_adadelta.prototxt
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue   423 Jun 22 16:55 mnist_autoencoder_solver_adagrad.prototxt
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue   466 Jun 22 16:55 mnist_autoencoder_solver_nesterov.prototxt
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue   433 Jun 22 16:55 mnist_autoencoder_solver.prototxt
drwxr--r-- 2 poddingue poddingue  4096 Jun 22 17:37 mnist_test_lmdb
drwxr--r-- 2 poddingue poddingue  4096 Jun 22 17:37 mnist_train_lmdb
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue 11948 Jun 22 16:55 readme.md
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt; 1 poddingue poddingue   106 Jun 22 16:55 train_lenet_adam.sh
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt; 1 poddingue poddingue   118 Jun 22 16:55 train_lenet_consolidated.sh
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt; 1 poddingue poddingue  4517 Jun 22 16:55 train_lenet_docker.sh
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt; 1 poddingue poddingue   115 Jun 22 16:55 train_lenet_rmsprop.sh
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt; 1 poddingue poddingue   101 Jun 22 16:55 train_lenet.sh
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt; 1 poddingue poddingue   120 Jun 22 16:55 train_mnist_autoencoder_adadelta.sh
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt; 1 poddingue poddingue   119 Jun 22 16:55 train_mnist_autoencoder_adagrad.sh
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt; 1 poddingue poddingue   120 Jun 22 16:55 train_mnist_autoencoder_nesterov.sh
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt; 1 poddingue poddingue   117 Jun 22 16:55 train_mnist_autoencoder.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;examples/mnist/create_mnist.sh&lt;/code&gt; has generated two folders in the &lt;code&gt;examples/mnist/&lt;/code&gt; directory, namely &lt;code&gt;mnist_train_lmdb&lt;/code&gt; and &lt;code&gt;mnist_test_lmdb&lt;/code&gt; after the LMDB data conversion succeeded. The &lt;code&gt;data.mdb&lt;/code&gt; and &lt;code&gt;lock.mdb&lt;/code&gt; stored in the folder are the running data we need.&lt;/p&gt;

&lt;h2&gt;
  
  
  LevelDB Data
&lt;/h2&gt;

&lt;p&gt;If you want to run leveldb data, run the program under &lt;code&gt;examples/siamese/&lt;/code&gt; folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; ./examples/siamese/create_mnist_siamese.sh
Creating leveldb...
Done.
ll examples/siamese/
total 204
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue   4368 Jun 22 16:55 convert_mnist_siamese_data.cpp
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt; 1 poddingue poddingue    617 Jun 22 16:55 create_mnist_siamese.sh
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue 158921 Jun 22 16:55 mnist_siamese.ipynb
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue   1488 Jun 22 16:55 mnist_siamese.prototxt
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue    810 Jun 22 16:55 mnist_siamese_solver.prototxt
drwxr-xr-x 2 poddingue poddingue   4096 Jun 22 17:39 mnist_siamese_test_leveldb
drwxr-xr-x 2 poddingue poddingue   4096 Jun 22 17:39 mnist_siamese_train_leveldb
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue   4907 Jun 22 16:55 mnist_siamese_train_test.prototxt
&lt;span class="nt"&gt;-rw-r--r--&lt;/span&gt; 1 poddingue poddingue   5949 Jun 22 16:55 readme.md
&lt;span class="nt"&gt;-rwxr-xr-x&lt;/span&gt; 1 poddingue poddingue    125 Jun 22 16:55 train_mnist_siamese.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next modify the configuration file lenet_solver.prototxt
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vi ./examples/mnist/lenet_solver.prototxt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As needed, set the maximum number of iterations at 100 and the last line, &lt;code&gt;solver_mode&lt;/code&gt;, to &lt;code&gt;CPU&lt;/code&gt;:&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="o"&gt;==&lt;/span&gt; The train/test net protocol buffer definition
net: &lt;span class="s2"&gt;"examples/mnist/lenet_train_test.prototxt"&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt; test_iter specifies how many forward passes the &lt;span class="nb"&gt;test &lt;/span&gt;should carry out.
&lt;span class="o"&gt;==&lt;/span&gt; In the &lt;span class="k"&gt;case&lt;/span&gt; of MNIST, we have &lt;span class="nb"&gt;test &lt;/span&gt;batch size 100 and 100 &lt;span class="nb"&gt;test &lt;/span&gt;iterations,
&lt;span class="o"&gt;==&lt;/span&gt; covering the full 10,000 testing images.
test_iter: 100
&lt;span class="o"&gt;==&lt;/span&gt; Carry out testing every 500 training iterations.
test_interval: 500
&lt;span class="o"&gt;==&lt;/span&gt; The base learning rate, momentum and the weight decay of the network.
base_lr: 0.01
momentum: 0.9
weight_decay: 0.0005
&lt;span class="o"&gt;==&lt;/span&gt; The learning rate policy
lr_policy: &lt;span class="s2"&gt;"inv"&lt;/span&gt;
gamma: 0.0001
power: 0.75
&lt;span class="o"&gt;==&lt;/span&gt; Display every 100 iterations
display: 100
&lt;span class="o"&gt;==&lt;/span&gt; The maximum number of iterations
max_iter: 10000
&lt;span class="o"&gt;==&lt;/span&gt; snapshot intermediate results
snapshot: 5000
snapshot_prefix: &lt;span class="s2"&gt;"examples/mnist/lenet"&lt;/span&gt;
&lt;span class="o"&gt;==&lt;/span&gt; solver mode: CPU or GPU
solver_mode: CPU
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Finally, run this example:
&lt;/h2&gt;

&lt;p&gt;root@OrangePi:~/caffe# time sh examples/mnist/train_lenet.sh I0110 06:28:06.117972 25078 caffe.cpp:197] Use CPU. I0110 06:28:06.118988 25078 solver.cpp:45] Initializing solver from parameters: test_iter: 100 test_interval: 500 base_lr: 0.01 display: 100 XUNLONG Software Co,. Ltd All rights reserved 36 &lt;a href="http://www.orangepi.com" rel="noopener noreferrer"&gt;www.orangepi.com&lt;/a&gt; max_iter: 10000 lr_policy: "inv" gamma: 0.0001 power: 0.75 momentum: 0.9 weight_decay: 0.0005 snapshot: 5000 snapshot_prefix: "examples/mnist/lenet" solver_mode: CPU net: "examples/mnist/lenet_train_test.prototxt"&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I0110 07:25:37.016746 25078 sgd_solver.cpp:284] Snapshotting solver state to binary proto file
examples/mnist/lenet_iter_10000.solverstate
I0110 07:25:37.146054 25078 solver.cpp:327] Iteration 10000, loss = 0.00200602
I0110 07:25:37.146195 25078 solver.cpp:347] Iteration 10000, Testing net (#0)
I0110 07:25:54.299634 25080 data_layer.cpp:73] Restarting data prefetching from start.
I0110 07:25:55.010598 25078 solver.cpp:414] Test net output #0: accuracy = 0.9913
I0110 07:25:55.010757 25078 solver.cpp:414] Test net output #1: loss = 0.0273203 (* 1 =
0.0273203 loss)
I0110 07:25:55.010777 25078 solver.cpp:332] Optimization Done.
I0110 07:25:55.010793 25078 caffe.cpp:250] Optimization Done.
real 57m48.984s
user 58m6.479s
sys 0m1.977s
⑦Operation conclusion
According to the above results, the running time of the CPU is about 58 minutes, with
an accuracy of about 99%.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>4b</category>
      <category>armbian</category>
      <category>machinelearning</category>
      <category>merrykombo</category>
    </item>
    <item>
      <title>🍊 ARM your continuous integration system with fruits! 🍌</title>
      <dc:creator>Bruno Verachten</dc:creator>
      <pubDate>Wed, 22 Apr 2026 07:44:14 +0000</pubDate>
      <link>https://dev.to/gounthar/arm-your-continuous-integration-system-with-fruits-2ocj</link>
      <guid>https://dev.to/gounthar/arm-your-continuous-integration-system-with-fruits-2ocj</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on January 11, 2021 at &lt;a href="https://bruno.verachten.fr/2021/01/11/arm-your-ci-with-fruits/" rel="noopener noreferrer"&gt;https://bruno.verachten.fr/2021/01/11/arm-your-ci-with-fruits/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;The goal of this howto is to show you how to add your own ARM Gitlab runner to your Gitlab instance Runners herd.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F261%2F1%2AXetah-hQRnbxV0rX_qLkuQ.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F261%2F1%2AXetah-hQRnbxV0rX_qLkuQ.gif" alt="Image for post" width="261" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Whaaaaaaaaaat?&lt;/p&gt;

&lt;p&gt;Ok, let me introduce quickly the concepts.&lt;/p&gt;

&lt;p&gt;Continuous integration allows the developers on a project to check that each change they made did not introduce any regression. Each commit will be followed by a build of their application, and by a run of their test suite. If every test runs still the same way after the commit, then all is fine. &lt;a href="https://gitlab.com" rel="noopener noreferrer"&gt;Gitlab&lt;/a&gt; is a web-based Git repository manager (with tons of features). You can use it directly online, install your own &lt;a href="https://gitlab.com/gitlab-org/gitlab-ce" rel="noopener noreferrer"&gt;community-edition&lt;/a&gt; copy, or &lt;a href="https://about.gitlab.com/pricing/" rel="noopener noreferrer"&gt;pay&lt;/a&gt; for top level support.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DXLBMbQG4u-Q" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DXLBMbQG4u-Q" alt="watch?v=XLBMbQG4u Q" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Today, we’ll be only interested in their &lt;em&gt;continuous integration&lt;/em&gt; feature.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F212%2F1%2ASa9nkYnFIuerM4xcVoYnJQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F212%2F1%2ASa9nkYnFIuerM4xcVoYnJQ.png" alt="Gitlab CI/CD logo" width="212" height="237"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Starting Continuous Integration (i.e. &lt;em&gt;CI&lt;/em&gt;) is really &lt;a href="https://docs.gitlab.com/ee/ci/quick_start/" rel="noopener noreferrer"&gt;straightforward&lt;/a&gt; with Gitlab. Just create a &lt;code&gt;.gitlab-ci.yml&lt;/code&gt; file in your repository hosted in Gitlab, populate it with the commands needed to build your application, and you’re good to go! 🎉 But wait… Where does that build and test execute? That’s where the runners enter. Runners are machines linked to Gitlab that will execute the builds. These can be real machines, virtual machines or containers. By default, you have access to shared runners on &lt;a href="http://gitlab.com" rel="noopener noreferrer"&gt;gitlab.com&lt;/a&gt; , or even on your company Gitlab instance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DiVWRp4ezNqY" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DiVWRp4ezNqY" alt="watch?v=iVWRp4ezNqY" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These runners are tagged (🏷️) so that you can also tag your builds accordingly. If you need a runner that will build a Docker image, it could be tagged &lt;code&gt;docker&lt;/code&gt; for example, and so would be your build step in your &lt;code&gt;.gitlab.yml\&lt;/code&gt;file. Your builds can now easily be customized without ruining your own machine configuration… and even use Docker.&lt;/p&gt;

&lt;h1&gt;
  
  
  Why would you add a 🏃?
&lt;/h1&gt;

&lt;p&gt;If you are using the main Gitlab shared instance, chances are that you will have to be patient to get the results of your build. The Shared Runner concept is really cool, as it allows to share the load, but … you’re not alone waiting in that queue… And the same could be true for your company’s Gitlab instance, as it will soon become popular. So you could add your own machine to the Gitlab Runners herd, just for your project. Pretty neat if you’re tired to wait for other people builds to finish. The problem is a new machine is not cheap. That’s way cool to have your own Gitlab Runner running in your office, but have a quick look at your pockets contents. Do you have enough cash to buy a new machine? Maybe not… But wait… In the introduction, I was talking about ARM Runners. Why would you need to add an ARM processor machine as a Gitlab Runner?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F500%2F1%2AO6QPb4jk0UsH6D2irsK5_w.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F500%2F1%2AO6QPb4jk0UsH6D2irsK5_w.gif" alt="Image for post" width="500" height="215"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The reason why I chose to install an ARM Gitlab Runner&lt;/p&gt;

&lt;p&gt;Well, if your code does not depend on a specific family of processors, there is no other cheaper alternative to build a new Gitlab Runner.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;\$15&lt;/em&gt; for an OrangePi &lt;a href="https://fr.aliexpress.com/store/product/New-Orange-Pi-Zero-H2-Quad-Core-Open-source-512MB-development-board-beyond-Raspberry-Pi/1553371_32761500374.html?spm=a2g0w.12010612.8148356.3.6ab5799dZxeXWO" rel="noopener noreferrer"&gt;Zero&lt;/a&gt; machine able to run Docker and Gitlab Runner can’t be beaten. And… if you want to develop for that kind of architecture (ARMV7, aarch64…), you will need that kind of runner. I am trying to port software to the ARM processor, and these ARM Gitlab Runners have proven to be really useful. I can develop on my favorite IDE (on a X86 machine for example), commit, and automagically, my modified code will be compiled and tested on an ARM machine. I have to admit all of this could also be done with X86 Runners, as &lt;a href="https://www.qemu.org/" rel="noopener noreferrer"&gt;QEMU&lt;/a&gt; is able to emulate ARM processors. And even the build of ARM Docker image can be done on X86 Gitlab Runners, thanks to the amazing work of guys at &lt;a href="https://www.balena.io/" rel="noopener noreferrer"&gt;Balena&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F267%2F1%2AXnLd2UQBFJIlThSzr1WZmA.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F267%2F1%2AXnLd2UQBFJIlThSzr1WZmA.gif" alt="Image for post" width="267" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But where’s the fun? And furthermore… We all have a Raspberry Pi taking layer after layer of dust. Who has not been bored after having his first led blink? Why not put it back to work?&lt;/p&gt;

&lt;p&gt;Let’s go then for a cheap and fun solution.&lt;/p&gt;

&lt;h1&gt;
  
  
  Choose your preferred fruit
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F800%2F1%2AtMFaZclJuz1aZ1ykV2ezVA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F800%2F1%2AtMFaZclJuz1aZ1ykV2ezVA.png" alt="🍊 Pi Zero" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this article, we’re not going to install Gitlab Runner on a 🍊 Pi, but on a &lt;a href="https://twitter.com/Poddingue/status/1017689162428895232" rel="noopener noreferrer"&gt;Raspberry Pi&lt;/a&gt; (because we had one laying around), but this should work on any &lt;a href="https://www.armbian.com/download/?specifications=docker" rel="noopener noreferrer"&gt;fruit&lt;/a&gt; (aka ARM Docker capable device), as &lt;a href="http://www.banana-pi.org/" rel="noopener noreferrer"&gt;🍌&lt;/a&gt; or &lt;a href="https://twitter.com/Poddingue/status/1022543760872366080" rel="noopener noreferrer"&gt;🍊&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F300%2F1%2AhH8ulrNvec1AE44yZxVj9Q.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F300%2F1%2AhH8ulrNvec1AE44yZxVj9Q.jpeg" alt="Raspberry Pi found on Amazon" width="300" height="193"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;image::&lt;a href="http://www.banana-pi.org/%5C%5B%5C%5B%F0%9F%8D%8C%5C" rel="noopener noreferrer"&gt;http://www.banana-pi.org/\[\[🍌\&lt;/a&gt;] Pi M1, courtesy of &lt;a href="https://www.armbian.com/bananapi/" rel="noopener noreferrer"&gt;Armbian&lt;/a&gt;](../assets/images/2021/02/04/bananapi.png)&lt;/p&gt;

&lt;p&gt;This version of Gitlab Runner we’ll use needs Docker, that’s why we need a Linux distribution which supports Docker. We’re using an &lt;a href="https://blog.hypriot.com/" rel="noopener noreferrer"&gt;Hypriot&lt;/a&gt; distro because of the Raspberry Pi. We could have used &lt;a href="https://www.raspberrypi.org/downloads/raspbian/" rel="noopener noreferrer"&gt;Raspbian&lt;/a&gt;, but HypriotOS is the best Docker-oriented distribution for the Raspberry Pi. From downloading the image to the first &lt;code&gt;docker \&lt;/code&gt;command will take only five minutes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F300%2F1%2A-9x8mY52ZJxiz77n6x6Chg.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F300%2F1%2A-9x8mY52ZJxiz77n6x6Chg.gif" alt="From downloading to running Docker in 5 minutes" width="300" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is also a 64bits fork (as lots of Raspberry Pi distributions are not available for 64bits, that’s a nice plus). To me, that’s the first choice if you want to use Docker on a Raspberry Pi out of the box.&lt;/p&gt;

&lt;p&gt;For all other devices, prefer &lt;a href="https://www.armbian.com/download/" rel="noopener noreferrer"&gt;Armbian&lt;/a&gt; if supported (like the Orange Pi Zero that can run as a Gitlab Runner too). It’s really easy to install &lt;a href="https://docs.armbian.com/User-Guide_Advanced-Features/#how-to-run-docker" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; on this distribution.&lt;/p&gt;

&lt;p&gt;First of all, let’s download the latest Hypriot distro.&lt;/p&gt;

&lt;h1&gt;
  
  
  Downloading and installing the Hypriot Distro
&lt;/h1&gt;

&lt;p&gt;Go to the Hypriot &lt;a href="https://blog.hypriot.com/downloads/" rel="noopener noreferrer"&gt;download&lt;/a&gt; page and choose the latest distro.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Current image available on 02/04/2021 is Version 1.12.3 : &lt;a href="https://github.com/hypriot/image-builder-rpi/releases/download/v1.12.3/hypriotos-rpi-v1.12.3.img.zip" rel="noopener noreferrer"&gt;https://github.com/hypriot/image-builder-rpi/releases/download/v1.12.3/hypriotos-rpi-v1.12.3.img.zip&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  On Windows
&lt;/h1&gt;

&lt;p&gt;Next go to your &lt;em&gt;Download&lt;/em&gt; folder and extract the downloaded zip file by right-clicking on the file and then clicking on &lt;em&gt;7-zip → extract here&lt;/em&gt;…. if you use 7-zip. Whatever your unzipping tool may be, right-click and choose the right option to uncompress the file. After 7-zip is finished with extracting you will have a file with a &lt;code&gt;.img&lt;/code&gt; extension.&lt;/p&gt;

&lt;h1&gt;
  
  
  On OsX
&lt;/h1&gt;

&lt;p&gt;It’s almost the same as for Windows, except that the tool could be &lt;a href="https://www.keka.io/en/" rel="noopener noreferrer"&gt;Keka&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  On Linux
&lt;/h1&gt;

&lt;p&gt;Just use 7z.&lt;/p&gt;

&lt;h1&gt;
  
  
  Writing the image
&lt;/h1&gt;

&lt;p&gt;In the next step we will flash this file onto your SD card. We have to use the finest piece of sofware able to write &lt;em&gt;CORRECTLY&lt;/em&gt; and &lt;em&gt;CHECK&lt;/em&gt; the written image (i.e. &lt;a href="https://etcher.io/" rel="noopener noreferrer"&gt;Etcher&lt;/a&gt;) on any platform. Current version is 1.4.4.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Windows: &lt;a href="https://github.com/balena-io/etcher/releases/download/v1.5.116/balenaEtcher-Setup-1.5.116.exe?d_id=175dc868ae93-02925a057a102f-1a327340-1fa400-175dc868aea1d0" rel="noopener noreferrer"&gt;https://github.com/balena-io/etcher/releases/download/v1.5.116/balenaEtcher-Setup-1.5.116.exe?d_id=175dc868ae93-02925a057a102f-1a327340-1fa400-175dc868aea1d0&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;osX: &lt;a href="https://github.com/balena-io/etcher/releases/download/v1.5.116/balenaEtcher-1.5.116.dmg?d_id=175dc868ae93-02925a057a102f-1a327340-1fa400-175dc868aea1d0" rel="noopener noreferrer"&gt;https://github.com/balena-io/etcher/releases/download/v1.5.116/balenaEtcher-1.5.116.dmg?d_id=175dc868ae93-02925a057a102f-1a327340-1fa400-175dc868aea1d0&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Linux: &lt;a href="https://github.com/balena-io/etcher/releases/download/v1.5.116/balena-etcher-electron-1.5.116-linux-x64.zip?d_id=175dc868ae93-02925a057a102f-1a327340-1fa400-175dc868aea1d0" rel="noopener noreferrer"&gt;https://github.com/balena-io/etcher/releases/download/v1.5.116/balena-etcher-electron-1.5.116-linux-x64.zip?d_id=175dc868ae93-02925a057a102f-1a327340-1fa400-175dc868aea1d0&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Too bad it does not exist for armv7. I do insist on the use of Etcher, because lots of supposed Linux bugs are because of a badly written image, or to a faulty SDCard. Etcher will take care of these issues. If possible, do not use any USB Dongle or gizmo to host the SDCard, prefer your laptop own SDCard reader. And for the SDCard, prefer a &lt;em&gt;REAL&lt;/em&gt; and &lt;em&gt;GENUINE&lt;/em&gt; SDCard.&lt;/p&gt;

&lt;p&gt;How can you differentiate a real one from a bad copy? Well, you can’t, or not easily… Anyway, unzip the image, and write it with Etcher to your SDCard. In the latests versions of Etcher, you don’t even need to unzip the image file beforehand.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F800%2F1%2AqLX-092qNGY-JcfHQIGQig.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F800%2F1%2AqLX-092qNGY-JcfHQIGQig.gif" alt="Etcher process" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Upon completion, close Etcher and eject the SD card before removing it from its slot. In order to eject the SD card, use the secure eject function in the menu next to your system clock. This is usually in the very bottom right corner of your screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F439%2F1%2ANl9P9s7CchJC1-Vu-F2zIw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F439%2F1%2ANl9P9s7CchJC1-Vu-F2zIw.png" alt="Eject SDCard" width="439" height="133"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now it’s time to start your Raspberry Pi with the SD card&lt;/p&gt;

&lt;h1&gt;
  
  
  Boot your Raspberry Pi
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Connect the Raspberry Pi to your local network via an Ethernet cable&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;put the SD card into the designated slot&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;plug in the power adapter&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After finishing the last step your Raspberry Pi will boot and the LEDs should start blinking. The very first boot will take one to three minutes as the file system will be resized.&lt;/p&gt;

&lt;h1&gt;
  
  
  Find its IP on the network
&lt;/h1&gt;

&lt;p&gt;After booting, you can find the Raspberry Pi at your network with a simple &lt;code&gt;ping black-pearl.local \&lt;/code&gt;— no more searching for IP addresses required thanks to the integrated Avahi service discovery.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1802%2F1%2AmghqK-yJmbIRv0ajDYfdYw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1802%2F1%2AmghqK-yJmbIRv0ajDYfdYw.png" alt="Image for post" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.githubusercontent.com/gounthar/754f2988dc9ee643ea1d8be9a9c42223/raw/5437d39cdb07ab6bfb536ae468b483d8c197a29a/ssh%20pirate.txt" rel="noopener noreferrer"&gt;https://gist.githubusercontent.com/gounthar/754f2988dc9ee643ea1d8be9a9c42223/raw/5437d39cdb07ab6bfb536ae468b483d8c197a29a/ssh%2520pirate.txt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see in the following &lt;code&gt;ifconfig&lt;/code&gt; command, the MAC Address of the Raspberry always starts with &lt;code&gt;B8&lt;/code&gt;. That could prove useful if ever this first method does not work.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1296%2F1%2AsC_cfNa1I4Y_NIjJZSQuaw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1296%2F1%2AsC_cfNa1I4Y_NIjJZSQuaw.png" alt="Image for post" width="800" height="901"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.githubusercontent.com/gounthar/ceb2680f38e613289f4e55b9e8c4f819/raw/0e7888a483783a92eba88f5d1f9996a47b54cea3/ifconfig.txt" rel="noopener noreferrer"&gt;https://gist.githubusercontent.com/gounthar/ceb2680f38e613289f4e55b9e8c4f819/raw/0e7888a483783a92eba88f5d1f9996a47b54cea3/ifconfig.txt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, what shall we do if the very first &lt;code&gt;ping/ssh `does not work with `black-pearl&lt;/code&gt;? Well, as the Raspberry Pis MAC Address always start with &lt;code&gt;B8&lt;/code&gt; we can use nmap and keep only the IPs whose attached MAC Address sport a &lt;code&gt;B8&lt;/code&gt;(yes, it’s gross). If your network starts at &lt;code&gt;10.XX.30.XX `and ends at `10.XX.50.XX&lt;/code&gt;, &lt;code&gt;ip_start&lt;/code&gt; would be 30 and &lt;code&gt;ip_end&lt;/code&gt; would be 50.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1954%2F1%2Asr4MYJR2SaQesLg8r5WXrw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1954%2F1%2Asr4MYJR2SaQesLg8r5WXrw.png" alt="Image for post" width="800" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.githubusercontent.com/gounthar/2b4372abd7a623f7cf670575a76026d1/raw/e27c56e792fdf73652b4b53d07fa6bb91df1b7c7/nmap.txt" rel="noopener noreferrer"&gt;https://gist.githubusercontent.com/gounthar/2b4372abd7a623f7cf670575a76026d1/raw/e27c56e792fdf73652b4b53d07fa6bb91df1b7c7/nmap.txt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This will give you a list of Raspberry IPs on which you will then be able to try to connect with&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1498%2F1%2AxMIFpYi-0JmG-NoyElhFXA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1498%2F1%2AxMIFpYi-0JmG-NoyElhFXA.png" alt="Image for post" width="800" height="203"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Machine name and user password
&lt;/h1&gt;

&lt;p&gt;The default user “pirate” (password “hypriot”) can be changed or removed before the first boot. Everything happens in &lt;code&gt;/boot/user-data&lt;/code&gt;. You can add your public SSH key, disable password logins and specify a different user account before booting your Raspberry Pi. WiFi can be customized and enabled to have Docker up and running through the air without attaching a keyboard and monitor. How cool is that? You can also change the name of the machine later on with &lt;code&gt;sudo raspi-config&lt;/code&gt;. The changes will have to be made in &lt;code&gt;Network Options/hostname&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F849%2F1%2Aj0rkCaEBkDxCzlsTjruMhQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F849%2F1%2Aj0rkCaEBkDxCzlsTjruMhQ.png" alt="Image for post" width="800" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once it’s done, reboot and enjoy your new machine name:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1734%2F1%2AanyIpGY7IcZQaxMc7MCOtA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1734%2F1%2AanyIpGY7IcZQaxMc7MCOtA.png" alt="Image for post" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.githubusercontent.com/gounthar/596bc88f23d98de76bb70b11eb66e47b/raw/13f2210c233b6e1dc4d8da31f72e9063cfd18271/ssh%20pirate%20arm%20runner.txt" rel="noopener noreferrer"&gt;https://gist.githubusercontent.com/gounthar/596bc88f23d98de76bb70b11eb66e47b/raw/13f2210c233b6e1dc4d8da31f72e9063cfd18271/ssh%2520pirate%2520arm%2520runner.txt&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Proxy settings
&lt;/h1&gt;

&lt;p&gt;If you’re like me, stuck behind a corporate proxy, you will have to set a few things before being able to update your machine and run Docker.&lt;/p&gt;

&lt;h1&gt;
  
  
  Package manager
&lt;/h1&gt;

&lt;p&gt;Edit &lt;code&gt;/etc/apt/apt.conf.d/10proxy&lt;/code&gt; and add your proxy (create it if it doesn’t exist yet):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1176%2F1%2AYO2yRq5sowAncx9kg65JZQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1176%2F1%2AYO2yRq5sowAncx9kg65JZQ.png" alt="Image for post" width="800" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.githubusercontent.com/gounthar/e3288a613845b18aee3fe1098422d59f/raw/2eb3c68c8f2b3473902763e2f2b22be79918eea6/acquire.txt" rel="noopener noreferrer"&gt;https://gist.githubusercontent.com/gounthar/e3288a613845b18aee3fe1098422d59f/raw/2eb3c68c8f2b3473902763e2f2b22be79918eea6/acquire.txt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;sudo apt-get update&lt;/code&gt; should prove that the configuration is working&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1666%2F1%2AAlHcN0pshfekudRahrvB8A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1666%2F1%2AAlHcN0pshfekudRahrvB8A.png" alt="Image for post" width="800" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.githubusercontent.com/gounthar/dc7829b564944c460e16a68870c13409/raw/afb35d6fe8731476befdf5f62f5513d4372f3f6a/apt%20update.txt" rel="noopener noreferrer"&gt;https://gist.githubusercontent.com/gounthar/dc7829b564944c460e16a68870c13409/raw/afb35d6fe8731476befdf5f62f5513d4372f3f6a/apt%2520update.txt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While you’re there, update the machine with &lt;code&gt;sudo apt-get upgrade&lt;/code&gt; to have the freshest Docker environment available.&lt;/p&gt;

&lt;h1&gt;
  
  
  Docker
&lt;/h1&gt;

&lt;p&gt;If you try to download a Docker image with &lt;code&gt;docker pull&lt;/code&gt; … it will fail because Docker doesn’t use the same proxy settings as &lt;code&gt;apt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F2048%2F1%2Ahkun6URrs0SUNF8JEhKOgg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F2048%2F1%2Ahkun6URrs0SUNF8JEhKOgg.png" alt="Image for post" width="800" height="190"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.githubusercontent.com/gounthar/8384bb56d7ef8a9380c1f0d43013162e/raw/4f1f2aa8837966fe040e88c9e5c1aefc48d9f17a/docker%20pull.txt" rel="noopener noreferrer"&gt;https://gist.githubusercontent.com/gounthar/8384bb56d7ef8a9380c1f0d43013162e/raw/4f1f2aa8837966fe040e88c9e5c1aefc48d9f17a/docker%2520pull.txt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So you will have to create a few directories and files to let Docker know it should use your proxy:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1278%2F1%2AK3JeXg1j_LuDgHJFqdALRA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1278%2F1%2AK3JeXg1j_LuDgHJFqdALRA.png" alt="Image for post" width="800" height="282"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.githubusercontent.com/gounthar/67dfa1f13e3d3064a02d17cb3ed9a51d/raw/3bb338cd496e0032f2472178f23fe96416eee0a2/docker%20http%20proxy.txt" rel="noopener noreferrer"&gt;https://gist.githubusercontent.com/gounthar/67dfa1f13e3d3064a02d17cb3ed9a51d/raw/3bb338cd496e0032f2472178f23fe96416eee0a2/docker%2520http%2520proxy.txt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/etc/systemd/system/docker.service.d/https-proxy.conf&lt;/code&gt; will contain:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F2004%2F1%2Abtk9NYt7I0kj2RerYIfrNA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F2004%2F1%2Abtk9NYt7I0kj2RerYIfrNA.png" alt="Image for post" width="800" height="165"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.githubusercontent.com/gounthar/d04bb35e54b87f1a97e7ef6493f746a3/raw/2af846f014e46c76df96f22f0cbdcbe70fd35926/proxy%20conf.txt" rel="noopener noreferrer"&gt;https://gist.githubusercontent.com/gounthar/d04bb35e54b87f1a97e7ef6493f746a3/raw/2af846f014e46c76df96f22f0cbdcbe70fd35926/proxy%2520conf.txt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;http `version of this file will have the same proxy settings (just remove the `S&lt;/code&gt; of &lt;code&gt;HTTPS&lt;/code&gt;). Once you’re done with the modifications, you will have to tell Docker to restart to take this into account:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F740%2F1%2Ab9iqNBZqH3bh1CXNUKrIGA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F740%2F1%2Ab9iqNBZqH3bh1CXNUKrIGA.png" alt="Image for post" width="740" height="414"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.githubusercontent.com/gounthar/107de82adde721e3f7803778b3e08ffb/raw/c0d8670a60efe2585880aaf07a11e398ee17d230/restart%20docker" rel="noopener noreferrer"&gt;https://gist.githubusercontent.com/gounthar/107de82adde721e3f7803778b3e08ffb/raw/c0d8670a60efe2585880aaf07a11e398ee17d230/restart%2520docker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then you’ll be able to check that your modifications are correct:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F2048%2F1%2A5Aw9XONC5xCoQNmyPLPmDQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F2048%2F1%2A5Aw9XONC5xCoQNmyPLPmDQ.png" alt="Image for post" width="800" height="176"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.githubusercontent.com/gounthar/2b1df86210ee515b1c074e7140d29b35/raw/bad834d6d2c23387ed8c5097fec0a52d29805056/properties.txt" rel="noopener noreferrer"&gt;https://gist.githubusercontent.com/gounthar/2b1df86210ee515b1c074e7140d29b35/raw/bad834d6d2c23387ed8c5097fec0a52d29805056/properties.txt&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Date
&lt;/h1&gt;

&lt;p&gt;Check that the date on your machine is correct by issuing date in the terminal. If it’s not correct, you will have some troubles with getting a Docker image. So, if the date/time are not right, you can set them up with:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1116%2F1%2Ai5I2L1DMfeQzFosP6xE81Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1116%2F1%2Ai5I2L1DMfeQzFosP6xE81Q.png" alt="Image for post" width="800" height="158"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.githubusercontent.com/gounthar/ecc647cb01fe16bcd2f439d492b1e663/raw/15c97e75b7f8164a92f19dfb834fc9a6381fea9c/date.txt" rel="noopener noreferrer"&gt;https://gist.githubusercontent.com/gounthar/ecc647cb01fe16bcd2f439d492b1e663/raw/15c97e75b7f8164a92f19dfb834fc9a6381fea9c/date.txt&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Gitlab Runner
&lt;/h1&gt;

&lt;p&gt;You already know Gitlab Runner, I’m sure you are already using some shared runners with your CI on your company’s gitlab. Most of the time, you don’t need to setup your own, but as there aren’t that many ARM gitlab runners for the time being… &lt;a href="https://forum.gitlab.com/t/updated-the-docker-image-of-gitlab-runner-for-arm/7477" rel="noopener noreferrer"&gt;Gitlab Runner&lt;/a&gt; has been ported to ARM in 2017 by &lt;a href="https://gitlab.com/ulm0" rel="noopener noreferrer"&gt;Mauricio Ugaz&lt;/a&gt; as it wasn’t available, and everyone needs gitlab-runner on Docker running on ARM, am I right?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: there are two versions available. Alpine and Ubuntu. As Alpine is the default image, when you run docker pull klud/gitlab-runner Alpine version will be downloaded.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  About the image
&lt;/h1&gt;

&lt;p&gt;This image is built for ARM devices, based on the official repo of the GitLab Runner. That’s a fantastic work that made my daily work really more enjoyable.&lt;/p&gt;

&lt;h1&gt;
  
  
  Overview
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Runner container setup&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You need to mount a volume into the gitlab-runner container in order to share the configuration. This could be done this way:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1548%2F1%2A5H7Ch6DPY0i3yvu5ZKscLQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1548%2F1%2A5H7Ch6DPY0i3yvu5ZKscLQ.png" alt="Image for post" width="800" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.githubusercontent.com/gounthar/925bd7f20136b68b7a1bf337b31ff9ac/raw/4a48f4353261d247e0696ef581cd829bc44242ad/docker%20run%20txt" rel="noopener noreferrer"&gt;https://gist.githubusercontent.com/gounthar/925bd7f20136b68b7a1bf337b31ff9ac/raw/4a48f4353261d247e0696ef581cd829bc44242ad/docker%2520run%2520txt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As we want to use the Docker executor, it is necessary to mount the Docker socket this way. Don’t forget to create &lt;code&gt;/etc/gitlab-runner \&lt;/code&gt;directory ahead of time:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1296%2F1%2AtBEuZu_v5u2MTQT9kH8Zgg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1296%2F1%2AtBEuZu_v5u2MTQT9kH8Zgg.png" alt="Image for post" width="800" height="367"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.githubusercontent.com/gounthar/9c76e1523d04a29d490703fe68c32793/raw/79db3c2dd7f67d1083c77a2ac1109fc6d9b53e50/docker%20run%20II.txt" rel="noopener noreferrer"&gt;https://gist.githubusercontent.com/gounthar/9c76e1523d04a29d490703fe68c32793/raw/79db3c2dd7f67d1083c77a2ac1109fc6d9b53e50/docker%2520run%2520II.txt&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Register runner
&lt;/h1&gt;

&lt;p&gt;Once the container is up and running, you can then register the runner on your GitLab server. Go to your project on your Gitlab instance, click on &lt;em&gt;Settings&lt;/em&gt;, then on &lt;em&gt;CI/CD&lt;/em&gt;, and click on &lt;em&gt;Expand&lt;/em&gt; next to &lt;em&gt;Runner settings&lt;/em&gt;. Scroll down to &lt;em&gt;Setup a specific Runner manually&lt;/em&gt;, and note the information regarding the &lt;em&gt;URL&lt;/em&gt; and the &lt;em&gt;token&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DiVWRp4ezNqY" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DiVWRp4ezNqY" alt="watch?v=iVWRp4ezNqY" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Specify the following URL during the Runner setup: &lt;a href="https://gitlab.com/" rel="noopener noreferrer"&gt;https://gitlab.com/&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use the following registration token during setup: &lt;code&gt;the_token_that_is_yours_and_yours_alone&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1244%2F1%2AgDGS8S2SxTGEqh5bL5v7pw.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1244%2F1%2AgDGS8S2SxTGEqh5bL5v7pw.gif" alt="Image for post" width="760" height="431"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So once you have the information you need (URL and token), you can then do this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F2048%2F1%2AqdEiNG1XrIWXcUSuJyJZKQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F2048%2F1%2AqdEiNG1XrIWXcUSuJyJZKQ.png" alt="Image for post" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.githubusercontent.com/gounthar/968c840f9a5ef8ee98edfbd6d6154fff/raw/3ea52f940b92282d7fa97ebc5e8ab0ff3fb6219e/register.txt" rel="noopener noreferrer"&gt;https://gist.githubusercontent.com/gounthar/968c840f9a5ef8ee98edfbd6d6154fff/raw/3ea52f940b92282d7fa97ebc5e8ab0ff3fb6219e/register.txt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unfortunately, with the latest versions of Docker from 19.03, you may get this message.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2F..%2Fassets%2Fimages%2F2021%2F02%2F04%2Fdocker%2520error.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2F..%2Fassets%2Fimages%2F2021%2F02%2F04%2Fdocker%2520error.png" alt="docker error" width="800" height="98"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With Docker 19.03, TLS is enabled by default, to use TLS you need to update the GitLab Runner configuration so that the certificates are shared between the service and build container. To do this, update your config.toml so that it looks like that.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2F..%2Fassets%2Fimages%2F2021%2F02%2F04%2Fconfig%2520toml%2520modification%2520docker%252019.03.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2F..%2Fassets%2Fimages%2F2021%2F02%2F04%2Fconfig%2520toml%2520modification%2520docker%252019.03.png" alt="config toml modification docker 19.03" width="800" height="542"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s check if this new runner can now be seen. Refresh your CI/CI settings, expand &lt;em&gt;Runner settings&lt;/em&gt;, and go down. You should now be able to see your brand new Gitlab-ci runner proudly sporting its ARM tag! 🤩&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DZF7_Dwp7ISc" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DZF7_Dwp7ISc" alt="watch?v=ZF7 Dwp7ISc" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;!--&lt;a href="https://miro.medium.com/max/1244/1*ahzX2cz-Pnr51VyBcKZ0VA.gif" rel="noopener noreferrer"&gt;Image for post&lt;/a&gt;-→&lt;/p&gt;

&lt;p&gt;Congrats!&lt;/p&gt;

&lt;p&gt;You could also do it this way:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1278%2F1%2ApdAfyHtHj_9FgmpEBbH6tA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1278%2F1%2ApdAfyHtHj_9FgmpEBbH6tA.png" alt="Image for post" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.githubusercontent.com/gounthar/d6ce6108e3f25a11a8c70bc74ab65e97/raw/aaa4af34343124e9d3dbed731cffd1dde6c33243/register%20II.txt" rel="noopener noreferrer"&gt;https://gist.githubusercontent.com/gounthar/d6ce6108e3f25a11a8c70bc74ab65e97/raw/aaa4af34343124e9d3dbed731cffd1dde6c33243/register%2520II.txt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So now you have your own ARM runner, ready to work.&lt;/p&gt;

&lt;h1&gt;
  
  
  Tip
&lt;/h1&gt;

&lt;p&gt;For my case, I am using the standard image to build my Docker images with this Runner, and it works just fine. Dockerfiles and info about Docker in Docker images for ARM are &lt;a href="https://gitlab.com/ulm0/docker-arm" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Troubleshooting
&lt;/h1&gt;

&lt;p&gt;In case you’re using docker in docker in the runner, you may experience some issues&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F2048%2F1%2ADgX3iSEvrsmVt3Ec9ewAEA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F2048%2F1%2ADgX3iSEvrsmVt3Ec9ewAEA.png" alt="Image for post" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.githubusercontent.com/gounthar/7fd4ce41ebdd8b79bf7dfe2472af7119/raw/a3d0b0ff8891609078695ea60de0fc69e7d76fa0/daemon.txt" rel="noopener noreferrer"&gt;https://gist.githubusercontent.com/gounthar/7fd4ce41ebdd8b79bf7dfe2472af7119/raw/a3d0b0ff8891609078695ea60de0fc69e7d76fa0/daemon.txt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So in order to address this issue you need to look within the config folder you mounted in the runner container. There is a config file which is&lt;code&gt;/etc/gitlab-runner/config.toml\&lt;/code&gt;. You need to add two lines (regarding the &lt;code&gt;privileged`and the `volumes)`and then restart the runner with `docker restart arm-runner&lt;/code&gt;. If you can’t find the file, it is maybe in &lt;code&gt;~/.runner/config.toml/&lt;/code&gt;; copy it in &lt;code&gt;/etc/gitlab-runner&lt;/code&gt; after editing it and restart docker.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1204%2F1%2A1n0krvtUogmuodKiOQRQaw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F1204%2F1%2A1n0krvtUogmuodKiOQRQaw.png" alt="Image for post" width="800" height="464"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.githubusercontent.com/gounthar/31d41be5afb4b2c3ce0c66dfc1f00f33/raw/b08565f575646763ca3f71321a625e47f3c3619b/config.toml" rel="noopener noreferrer"&gt;https://gist.githubusercontent.com/gounthar/31d41be5afb4b2c3ce0c66dfc1f00f33/raw/b08565f575646763ca3f71321a625e47f3c3619b/config.toml&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So now you can enjoy your first ARM Gitlab build!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F2048%2F1%2AFKOoVagyEh6SGb63ogWALA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmiro.medium.com%2Fmax%2F2048%2F1%2AFKOoVagyEh6SGb63ogWALA.png" alt="Image for post" width="800" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We now have an ARM machine sporting a recent kernel, running a recent version of gitlab-runner on top of a recent version of Docker.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2F..%2Fassets%2Fimages%2F2021%2F02%2F04%2Fgitlab%2520runner%2520arm%2520version.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2F..%2Fassets%2Fimages%2F2021%2F02%2F04%2Fgitlab%2520runner%2520arm%2520version.png" alt="gitlab runner arm version" width="394" height="105"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2F..%2Fassets%2Fimages%2F2021%2F02%2F04%2Fhypriot%2520docker%2520version.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2F..%2Fassets%2Fimages%2F2021%2F02%2F04%2Fhypriot%2520docker%2520version.png" alt="hypriot docker version" width="432" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2F..%2Fassets%2Fimages%2F2021%2F02%2F04%2Fhypriot%2520screenfetch.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2F..%2Fassets%2Fimages%2F2021%2F02%2F04%2Fhypriot%2520screenfetch.png" alt="hypriot screenfetch" width="535" height="263"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have reached the first level of achievement, having Gitlab runner running on the Pi thanks to Docker. Can we go any further? Could we build Docker images thanks to Gitlab runner on the Pi?&lt;/p&gt;

&lt;h1&gt;
  
  
  Building Docker Images
&lt;/h1&gt;

&lt;p&gt;If you’re going to build images on this runner you can use the standard Docker image, as it is multi-arch now. Just type docker at “Please enter the Docker image” in the first method or in &lt;code&gt;--docker- image "image:tag"&lt;/code&gt; with the second method. Sometimes, you may experience some troubles when using Docker in Docker. So in order to address this issue you need to look within the config folder you mounted in the runner container. There is a config file which is &lt;code&gt;/etc/gitlab-runner/config.toml&lt;/code&gt;. You need to add two lines (regarding the privileged and the volumes)and then restart the runner with docker restart arm-runner.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2F..%2Fassets%2Fimages%2F2021%2F02%2F04%2Fprivileged.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2F..%2Fassets%2Fimages%2F2021%2F02%2F04%2Fprivileged.png" alt="privileged" width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you can’t find the file, it is maybe in &lt;code&gt;~/.runner/config.toml/&lt;/code&gt; copy it in &lt;code&gt;/etc/gitlab-runner&lt;/code&gt; after editing it and restart docker.&lt;/p&gt;

&lt;p&gt;It looks like we’ve reached the second level of achievement, being able to build Docker images through Gitlab runner.&lt;/p&gt;

&lt;p&gt;What about another challenge?&lt;/p&gt;

&lt;h1&gt;
  
  
  Super tiny Runner
&lt;/h1&gt;

&lt;p&gt;Let’s try it on another SBC, much, much cheaper, with half the amount of memory. This time, we’ll change fruit, let’s leave the raspberry aside and try with an Orange… Do you think it could work?&lt;/p&gt;

&lt;p&gt;The Orange Pi Zero is one of my preferred SBCs, and almost the cheapest to this date.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2F..%2Fassets%2Fimages%2F2021%2F02%2F04%2Forangepizero.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2F..%2Fassets%2Fimages%2F2021%2F02%2F04%2Forangepizero.png" alt="orangepizero" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’ll get if for about 9€, and it can run Docker with a very recent kernel thanks to the Armbian distro. This distro is a fantastic community project which allows lots of SBCs to run recent Linux kernels with optimizations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2F..%2Fassets%2Fimages%2F2021%2F02%2F04%2Farmbian.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2F..%2Fassets%2Fimages%2F2021%2F02%2F04%2Farmbian.png" alt="armbian" width="565" height="143"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.armbian.com/" rel="noopener noreferrer"&gt;Armbian&lt;/a&gt; is a lightweight Debian based distribution specialized for ARM developing boards. It’s Compiled from scratch, It has powerful build and software development tools, and a vibrant community to boot. It supports lots of chips like the Allwinner H2+, H3, H5, Amlogic S905, Rockchip RK3288 and RK3399, Samsung Exynos 5422… If we go to the download section you can see there are tons of different cards that can run Armbian.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D7JyZGcYTL00" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3D7JyZGcYTL00" alt="watch?v=7JyZGcYTL00" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;!-- INSERT HERE DOWNLOAD SECTION VIDEO -→&lt;/p&gt;

&lt;p&gt;It you choose OrangePi zero, you can see the specifications. image::&lt;a href="https://www.youtube.com/watch?v=dX9I6xDn5AE%5C%5B%5C%5D" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=dX9I6xDn5AE\[\]&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of them is Docker, which means you can run Docker on this board with Armbian.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DdX9I6xDn5AE" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DdX9I6xDn5AE" alt="watch?v=dX9I6xDn5AE" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;!-- INSERT HERE ORANGE PI ZERO ARMBIAN VIDEO -→&lt;/p&gt;

&lt;p&gt;Yes, you can run Docker on a 512MB ARM machine. You can even run gitlab runner on Docker on it. And even Docker on gitlab runner on Docker on it… Il you click on Docker, you will then be able to see the list of SBCs able to run Docker with Armbian.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DTnE1upS-QlQ" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DTnE1upS-QlQ" alt="watch?v=TnE1upS QlQ" width="" height=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&amp;lt;!-- INSERT HERE DOCKER MACHINES ARMBIAN VIDEO -→&lt;/p&gt;

&lt;p&gt;Impressive, isn’t it?&lt;/p&gt;

&lt;p&gt;Docker does not come preinstalled with Armbian, so you have to install it like that.&lt;/p&gt;

&lt;p&gt;image::../assets/images/2021/02/04/docker install on armbian.png[docker install on armbian]Note that you could get Docker preinstalled, as you can build this distro by yourself as long as you have an Ubuntu machine available. The rest of the commands to run gitlab runner are strictly the same than for Raspberry Pi, so no need to repeat them. Let’s just have a look at the result, once the runner has been installed, registered and started. Feel like taking a look ?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2F..%2Fassets%2Fimages%2F2021%2F02%2F04%2Fdocker%2520on%2520docker%2520runner.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2F..%2Fassets%2Fimages%2F2021%2F02%2F04%2Fdocker%2520on%2520docker%2520runner.gif" alt="docker on docker runner" width="960" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is a build of a Docker image on an Orange Pi Zero. Isn’t it fantastic? We reached the third level of Achievement…&lt;/p&gt;

&lt;h1&gt;
  
  
  64 bits Runner
&lt;/h1&gt;

&lt;p&gt;Some of the boards handled by Armbian or HypriotOS are ARM64 also called aarch64, or ARMV8. To tell you the truth, even the Raspberry Pi 3B is aarch64… It’s just that Raspbian does not handle it for compatibility reasons with their older boards. You can find 64 bits distros for the Raspberry Pi, like &lt;a href="https://itsfoss.com/install-arch-raspberry-pi/" rel="noopener noreferrer"&gt;ArchLinux&lt;/a&gt;, &lt;a href="https://dietpi.com/docs/" rel="noopener noreferrer"&gt;DietPi&lt;/a&gt;, &lt;a href="https://blog.hypriot.com/downloads/" rel="noopener noreferrer"&gt;Hypriot&lt;/a&gt;… For other cards, Armbian does exist for &lt;a href="https://www.armbian.com/download/?specifications=64bit" rel="noopener noreferrer"&gt;ARM64&lt;/a&gt;, so does &lt;a href="https://docs.docker.com/engine/install/binaries/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt;. Until 10 months ago, we were kind of stuck, as gitlab-runner for aarch64 did not exist image::&lt;a href="https://www.youtube.com/watch?v=dfP3wjcpveY%5C%5B%5C%5D" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=dfP3wjcpveY\[\]&lt;/a&gt; &amp;lt;!-- INSERT HERE MERGE REQUEST VIDEO -→&lt;/p&gt;

&lt;p&gt;The merge &lt;a href="https://gitlab.com/gitlab-org/gitlab-runner/-/merge_requests/725" rel="noopener noreferrer"&gt;request&lt;/a&gt; had been running for almost 3 years until it got accepted. We are now able to use gitlab-runner on our 64 bits ARM machine, and without using Mauricio Ugaz &lt;a href="https://gitlab.com/ulm0/gitlab-runner" rel="noopener noreferrer"&gt;fork&lt;/a&gt;, just the standard Docker image directly supplied by gitlab.&lt;/p&gt;

&lt;p&gt;To sum up, we managed to setup a gitlab-ci Runner on a Raspberry Pi, and then on an OrangePi Zero . You now know how to transform your dusty Raspi into a gitlab- runner, even able to build Docker images. In the company I work for, we have also installed it on other SBCs (Odroid XU4, Nano Pi Fire3 ), and on a ThunderX2 just for fun. We are using them everyday, as they are cheap, disposable, fun to use, and have a very small footprint, so that we install them just about anywhere. I hope to have tickled your curiosity regarding these little beasts called SBCs, and that some of you will join the peaceful ARM rebellion.&lt;/p&gt;

</description>
      <category>armbian</category>
      <category>ci</category>
      <category>hypriot</category>
      <category>orangepi</category>
    </item>
    <item>
      <title>Installing asciicast2gif on aarch64 Debian bullseye</title>
      <dc:creator>Bruno Verachten</dc:creator>
      <pubDate>Wed, 22 Apr 2026 07:44:12 +0000</pubDate>
      <link>https://dev.to/gounthar/installing-asciicast2gif-on-aarch64-debian-bullseye-41m8</link>
      <guid>https://dev.to/gounthar/installing-asciicast2gif-on-aarch64-debian-bullseye-41m8</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on August 20, 2021 at &lt;a href="https://bruno.verachten.fr/2021/08/20/installing-asciicast-on-aarch64/" rel="noopener noreferrer"&gt;https://bruno.verachten.fr/2021/08/20/installing-asciicast-on-aarch64/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  asciicast2gif: one way to produce a GIF file from your terminal session
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Why?
&lt;/h1&gt;

&lt;p&gt;Last year, I wrote a blog post about producing SVG and GIF files from a recording of terminal sessions thanks to &lt;a href="https://dev.to/2020/01/03/Terminal-recording-on-the-OrangePi-Zero/"&gt;termtosvg&lt;/a&gt;. This makes sense if you want to illustrate your tutorials with nice moving pictures of what’s going on on the machine. You can see an example of the result of this process just at the beginning of the post. Why should you use a GIF file, an old (1987, same year as PowerPoint!) and bulky format with a very limited color palette instead of SVG? Well, you shouldn’t, but sometimes you have to. Or you think you have to.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F08%2F20%2Fobama-what.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F08%2F20%2Fobama-what.gif" alt="obama-what" width="480" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Okay, sometimes you really have to, like when using a meme, or posting something on Medium (because yes, Medium can’t (or don’t want to) handle SVG). My problem is now solved, as I sort of got kicked out of Medium, without further explanation (This account is under investigation or was found in violation of the &lt;a href="https://policy.medium.com/medium-rules-30e5502c4eb4" rel="noopener noreferrer"&gt;Medium Rules&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Now what? There may be other ways to generate GIF files from SVG or from the &lt;code&gt;.cast&lt;/code&gt; recordings, or directly when recording the terminal, but today, we’ll do it with &lt;a href="https://github.com/asciinema/asciicast2gif" rel="noopener noreferrer"&gt;asciicast2gif&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;asciicast2gif is, as its name implies, some kind of converter that will take a &lt;code&gt;. cast&lt;/code&gt; file as an input, and produce a GIF file. Last time I tried to install asciicast2gif on an ARM machine, it was a painful experience.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F08%2F20%2Fpain-in-the-rear-football.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F08%2F20%2Fpain-in-the-rear-football.gif" alt="pain-in-the-rear-football" width="480" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The thing is, one of the most important dependency is &lt;a href="https://phantomjs.org/" rel="noopener noreferrer"&gt;PhantomJS&lt;/a&gt; whose development is suspended, so don’t expect to get a standard package available in your &lt;a href="https://www.armbian.com/download/" rel="noopener noreferrer"&gt;ARM distro&lt;/a&gt; anytime. I tried several times to get it to compile, but failed. Furthermore, when you manage to install it, you’d better be patient, because it is really CPU hungry, and even the smallest GIF rendering will takes ages on a powerful machine.&lt;/p&gt;

&lt;p&gt;You’re now convinced that you need it, so why not try it together?&lt;/p&gt;

&lt;h1&gt;
  
  
  How?
&lt;/h1&gt;

&lt;p&gt;This time, I did not try to install asciicast2gif on an ARMV7 machine, nor on a X86_64 machine, but on an ARMV8, the amazing &lt;a href="https://www.youtube.com/watch?v=TS1nYM260XI" rel="noopener noreferrer"&gt;StationP1&lt;/a&gt; from &lt;a href="https://www.firefly.store/goods.php?id=114" rel="noopener noreferrer"&gt;Firefly&lt;/a&gt;. It sports a Rockchip RK3399, a 32GB eMMC, 4GB of RAM and a very sturdy box which doubles as a very efficient heatsink. I own and use a few RK3399-based machine, and it’s the most successful one. This thing just works, and it rocks!&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;p&gt;To get asciicast2gif to work, you will need a JRE, &lt;a href="https://github.com/kohler/gifsicle" rel="noopener noreferrer"&gt;gifsicle&lt;/a&gt;, &lt;a href="https://imagemagick.org/index.php" rel="noopener noreferrer"&gt;ImageMagick&lt;/a&gt;, &lt;a href="https://nodejs.org/en/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; and &lt;a href="https://www.npmjs.com/" rel="noopener noreferrer"&gt;npm&lt;/a&gt;. Fortunately, everything is available straight from &lt;a href="https://www.armbian.com/kernel/" rel="noopener noreferrer"&gt;Armbian&lt;/a&gt;.&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="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; gifsicle imagemagick nodejs npm openjdk-11-jre
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will also need to install &lt;a href="https://github.com/technomancy/leiningen/" rel="noopener noreferrer"&gt;leiningen&lt;/a&gt; in your &lt;code&gt;PATH&lt;/code&gt;, so that you can automate Clojure projects without setting your hair on fire.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F08%2F20%2Fbeaker-fire.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F08%2F20%2Fbeaker-fire.gif" alt="beaker-fire" width="350" height="430"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chmod&lt;/span&gt; +x lein &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo mv &lt;/span&gt;lein /usr/local/bin/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I told you earlier that I failed several times at building PhantomJS on anything ARM. Once more, I failed today. You could hope that I would get wiser as I’m getting older, but it looks like it’s the other way around. So I took the easy way out. I downloaded a pre-build AARCH64 binary.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F08%2F20%2Fno-guts-no-glory-right-tough.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F08%2F20%2Fno-guts-no-glory-right-tough.gif" alt="no-guts-no-glory-right-tough" width="498" height="498"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; wget https://github.com/fg2it/phantomjs-on-raspberry/releases/download/v2.1.1-jessie-stretch-arm64/phantomjs_2.1.1_arm64.tgz &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xvzf&lt;/span&gt; phantomjs_2.1.1_arm64.tgz &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo mv &lt;/span&gt;phantomjs /usr/local/bin/ &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm &lt;/span&gt;phantomjs_2.1.1_arm64.tgz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;Now that all the prerequisites are installed, let’s go straight to the point:&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="nb"&gt;cd&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git clone &lt;span class="nt"&gt;--recursive&lt;/span&gt; https://github.com/asciinema/asciicast2gif.git &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;asciicast2gif/
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; lein cljsbuild once main &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; lein cljsbuild once page
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks like we’re done? On this machine, we’re not. If I had an &lt;a href="https://store.avantek.co.uk/ampere-emag-64bit-arm-workstation.html" rel="noopener noreferrer"&gt;ARM Workstation&lt;/a&gt; that would be ok. But as we’re working on a machine with very limited capacity (disk and RAM), we have to make a few adjustments.&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="o"&gt;==&lt;/span&gt; Need more room &lt;span class="k"&gt;for &lt;/span&gt;generating GIFs
&lt;span class="nb"&gt;sudo &lt;/span&gt;vi /etc/ImageMagick-6/policy.xml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this file, you will have to change a value of &lt;code&gt;1GiB&lt;/code&gt; to &lt;code&gt;8GiB&lt;/code&gt; so that you won’t get an error message after a long time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;[...]
== from   &lt;span class="nt"&gt;&amp;lt;policy&lt;/span&gt; &lt;span class="na"&gt;domain=&lt;/span&gt;&lt;span class="s"&gt;"resource"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"disk"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"1GiB"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt; to
&lt;span class="nt"&gt;&amp;lt;policy&lt;/span&gt; &lt;span class="na"&gt;domain=&lt;/span&gt;&lt;span class="s"&gt;"resource"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"disk"&lt;/span&gt; &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"8GiB"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
[...]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will also have to temporarily grow the size of the partition hosting &lt;code&gt;/tmp&lt;/code&gt; if you don’t want to face &lt;code&gt;convert-im6.q16: unable to write pixel cache "/tmp/magick-doISVFWvo4dMZbegtgZqkiee3mpVUZ6N": No space left on device @ error/cache.c/WritePixelCachePixels/6012.&lt;/code&gt;&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="nb"&gt;sudo &lt;/span&gt;mount &lt;span class="nt"&gt;-o&lt;/span&gt; remount,size&lt;span class="o"&gt;=&lt;/span&gt;8G,noexec,nosuid,nodev,noatime /tmp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit the /etc/fstab file to make this changes permanently.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vim /etc/fstab
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;==&lt;/span&gt; &amp;lt;file system&amp;gt;                                 &amp;lt;mount point&amp;gt;   &amp;lt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;  &amp;lt;options&amp;gt;                                                       &amp;lt;dump&amp;gt;  &amp;lt;pass&amp;gt;
tmpfs                                           /tmp            tmpfs   &lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8G,defaults,nosuid                                                 0       0
&lt;span class="nv"&gt;UUID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;147a0457-31cc-44c0-b92a-a9a6b2305749       /               ext4    defaults,noatime,nodiratime,commit&lt;span class="o"&gt;=&lt;/span&gt;600,errors&lt;span class="o"&gt;=&lt;/span&gt;remount-ro,x-gvfs-hide    0       1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we’re good to go.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./asciicast2gif &lt;span class="nt"&gt;-s&lt;/span&gt; 2 &lt;span class="nt"&gt;-t&lt;/span&gt; solarized-dark ../termtosvg/whatever-name-I-choose.cast demo.gif
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F08%2F20%2Fdemo.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F08%2F20%2Fdemo.gif" alt="demo" width="2912" height="1944"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nice, isn’t it? Launch the command, and then go brew a coffee. Or even better, grab some fresh coffee seeds, sow them, wait a few years, and then look at the result while brewing a coffee with the harvested seeds. The &lt;a href="https://github.com/asciinema/asciicast2gif#usage" rel="noopener noreferrer"&gt;usage&lt;/a&gt; is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;asciicast2gif &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;-t&lt;/span&gt; theme] &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;-s&lt;/span&gt; speed] &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;-S&lt;/span&gt; scale] &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;-w&lt;/span&gt; cols] &lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;-h&lt;/span&gt; rows] &amp;lt;input-json-path-or-url&amp;gt; &amp;lt;output-gif-path&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I chose the &lt;code&gt;solarized-dark&lt;/code&gt; theme, but I could have gone with several others:&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="nt"&gt;-t&lt;/span&gt; &amp;lt;theme&amp;gt;        color theme, one of: asciinema, tango, solarized-dark, solarized-light, monokai &lt;span class="o"&gt;(&lt;/span&gt;default: asciinema&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nt"&gt;-s&lt;/span&gt; &amp;lt;speed&amp;gt;        animation speed &lt;span class="o"&gt;(&lt;/span&gt;default: 1&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nt"&gt;-S&lt;/span&gt; &amp;lt;scale&amp;gt;        image scale / pixel density &lt;span class="o"&gt;(&lt;/span&gt;default: 2&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nt"&gt;-w&lt;/span&gt; &amp;lt;columns&amp;gt;      clip terminal to specified number of columns &lt;span class="o"&gt;(&lt;/span&gt;width&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nt"&gt;-h&lt;/span&gt; &amp;lt;rows&amp;gt;         clip terminal to specified number of rows &lt;span class="o"&gt;(&lt;/span&gt;height&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  TL;DR
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; gifsicle imagemagick nodejs npm openjdk-11-jre
wget https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chmod&lt;/span&gt; +x lein &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo mv &lt;/span&gt;lein /usr/local/bin/
&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; wget https://github.com/fg2it/phantomjs-on-raspberry/releases/download/v2.1.1-jessie-stretch-arm64/phantomjs_2.1.1_arm64.tgz &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;tar&lt;/span&gt; &lt;span class="nt"&gt;-xvzf&lt;/span&gt; phantomjs_2.1.1_arm64.tgz &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;sudo mv &lt;/span&gt;phantomjs /usr/local/bin/ &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;rm &lt;/span&gt;phantomjs_2.1.1_arm64.tgz
&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git clone &lt;span class="nt"&gt;--recursive&lt;/span&gt; https://github.com/asciinema/asciicast2gif.git &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;asciicast2gif/
npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; lein cljsbuild once main &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; lein cljsbuild once page
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then edit &lt;code&gt;sudo vi /etc/ImageMagick-6/policy.xml&lt;/code&gt; and replace &lt;code&gt;&amp;lt;policy domain="resource" name="disk" value="1GiB"/&amp;gt;&lt;/code&gt; with &lt;code&gt;&amp;lt;policy domain="resource" name="disk" value="8GiB"/&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then grow the &lt;code&gt;tmp`partition `sudo mount -o remount,size=8G,noexec,nosuid,nodev,noatime /tmp&lt;/code&gt; and you’re ready to go :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./asciicast2gif &lt;span class="nt"&gt;-s&lt;/span&gt; 2 &lt;span class="nt"&gt;-t&lt;/span&gt; solarized-dark ../termtosvg/whatever-name-I-choose.cast demo.gif
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F08%2F20%2Fobama-finger-guns.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F08%2F20%2Fobama-finger-guns.gif" alt="obama-finger-guns" width="498" height="490"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>aarch64</category>
      <category>armbian</category>
      <category>bullseye</category>
      <category>firefly</category>
    </item>
    <item>
      <title>Installing libltc on armhf Debian</title>
      <dc:creator>Bruno Verachten</dc:creator>
      <pubDate>Wed, 22 Apr 2026 07:44:10 +0000</pubDate>
      <link>https://dev.to/gounthar/installing-libltc-on-armhf-debian-37if</link>
      <guid>https://dev.to/gounthar/installing-libltc-on-armhf-debian-37if</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on August 20, 2021 at &lt;a href="https://bruno.verachten.fr/2021/08/20/installing-libltc-on-armhf/" rel="noopener noreferrer"&gt;https://bruno.verachten.fr/2021/08/20/installing-libltc-on-armhf/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Installing libltc on armhf Debian
&lt;/h1&gt;

&lt;p&gt;One of my (way too many) current projects is to create a device that would mimic &lt;a href="https://dish.tc/faq/" rel="noopener noreferrer"&gt;dish.tc&lt;/a&gt;. I have created a &lt;a href="https://gitlab.com/gounthar/time-whisperer" rel="noopener noreferrer"&gt;repository&lt;/a&gt; at Gitlab so that anyone can help make this idea come true.&lt;/p&gt;

&lt;p&gt;To sum up, we would add an audio track to every recording we make (be it audio or video or both) so that &lt;a href="https://www.youtube.com/watch?v=9qjrMeCW1-c" rel="noopener noreferrer"&gt;video editing software&lt;/a&gt; would then be able to synchronize everything without any human intervention.&lt;/p&gt;

&lt;p&gt;I’m not sure yet of the SBC I will use for that job. For sure, I would like to use the OrangePi &lt;a href="https://fr.aliexpress.com/item/1005001688034180.html?spm=a2g0o.store_pc_groupList.8148356.7.73dd94fb03pmOp" rel="noopener noreferrer"&gt;Zero&lt;/a&gt;, as it is cheap (as I am), or any other SBC, as long as it is handled by &lt;a href="https://www.armbian.com/download/?device_support=Supported" rel="noopener noreferrer"&gt;Armbian&lt;/a&gt; and has good audio features (yes, I know the Zero doesn’t have great &lt;a href="https://fr.aliexpress.com/item/32770665186.html?spm=a2g0o.store_pc_groupList.8148356.1.7f667fddpaPeIL" rel="noopener noreferrer"&gt;audio&lt;/a&gt; features nor &lt;a href="https://forum.armbian.com/topic/6021-orange-pi-zero-i2s/?do=findComment&amp;amp;comment=52342" rel="noopener noreferrer"&gt;I2S&lt;/a&gt;, but I already have it, so…​) The first step is then to install &lt;a href="https://github.com/x42/libltc" rel="noopener noreferrer"&gt;libltc&lt;/a&gt; which is not yet part of Debian for ARM.&lt;/p&gt;

&lt;h1&gt;
  
  
  Installation
&lt;/h1&gt;

&lt;p&gt;If you’re lucky, the package will be available from your package repo manager:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F09%2F01%2Fapt-install.cast.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F09%2F01%2Fapt-install.cast.svg" alt="apt-install.cast" width="1600" height="1022.0"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;libtlc11
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Should you not find it, then we’ll install it from source:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F09%2F01%2Fclone.cast.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F09%2F01%2Fclone.cast.svg" alt="clone.cast" width="1600" height="1022.0"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/x42/libltc.git
&lt;span class="nb"&gt;cd &lt;/span&gt;libltc/
aclocal&lt;span class="p"&gt;;&lt;/span&gt; autoheader&lt;span class="p"&gt;;&lt;/span&gt; libtoolize &lt;span class="nt"&gt;--copy&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; autoconf&lt;span class="p"&gt;;&lt;/span&gt; automake &lt;span class="nt"&gt;--gnu&lt;/span&gt; &lt;span class="nt"&gt;--add-missing&lt;/span&gt; &lt;span class="nt"&gt;--copy&lt;/span&gt;
./configure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F09%2F01%2Fconfigure.cast.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F09%2F01%2Fconfigure.cast.svg" alt="configure.cast" width="1600" height="1022.0"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;...]
 libltc configured:
 &lt;span class="nt"&gt;-----------------------&lt;/span&gt;

  version:             1.3.1
  interface revision:  12:0:1

  doxygen:             no
  installation prefix: /usr/local

 &lt;span class="nb"&gt;type&lt;/span&gt; &lt;span class="s2"&gt;"make"&lt;/span&gt; followed my &lt;span class="s2"&gt;"make install"&lt;/span&gt; as root.
 run &lt;span class="s2"&gt;"make check"&lt;/span&gt; to perform selftests.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ok, let’s do that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make check
&lt;span class="nb"&gt;sudo &lt;/span&gt;make &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F09%2F01%2Fmake.cast.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F09%2F01%2Fmake.cast.svg" alt="make.cast" width="1600" height="1022.0"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F09%2F01%2Fmake-install.cast.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F09%2F01%2Fmake-install.cast.svg" alt="make-install.cast" width="1600" height="1022.0"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ok, perfect, the library is now installed on our &lt;code&gt;armhf&lt;/code&gt; machine. Now what?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F09%2F01%2Flibltc11-bullseye-armhf-files.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2021%2F09%2F01%2Flibltc11-bullseye-armhf-files.png" alt="libltc11-bullseye-armhf-files" width="800" height="62"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Yes, it’s a library and not a standalone program. You can now develop something that will use this library, or …​ install other tools that give you the opportunity to generate SMTPE encoded in sounds or sound files. One of them is &lt;a href="https://github.com/x42/ltc-tools" rel="noopener noreferrer"&gt;ltc-tools&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Installing ltc-tools&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Unfortunately this time, this tool hasn't made yet its way to the official Debian repositories, so you'll have to build it by yourself.

image::/assets/images/2021/09/01/ltc-tools-clone.cast.svg[ltc-tools-clone.cast]

[source,bash]
----
cd &amp;amp;&amp;amp; git clone https://github.com/x42/ltc-tools.git

----

ltc-tools is already packaged to build as a Debian package, so let's install its dependencies

image::/assets/images/2021/09/01/ltc-tools-dependencies.cast.svg[ltc-tools-dependencies.cast]

[source,bash]
----
cd ltc-tools &amp;amp;&amp;amp; sudo apt install debhelper dpkg-dev fakeroot libjack-jackd2-dev libltc-dev libsndfile1-dev

----

Now, let's build the package:

image::/assets/images/2021/09/01/ltc-tools-build-package.cast.svg[ltc-tools-build-package.cast]

[source,bash]
----
dpkg-buildpackage -rfakeroot -b -uc -us
ll ../*ltc*deb

----

We just have to install the package now, almost as if it was coming from the official Debian repo:

image::/assets/images/2021/09/01/ltc-tools-install-package.cast.svg[ltc-tools-install-package.cast]

[source,bash]
----
sudo apt install -f ../ltc-tools*deb

----

What are the binaries available by the way?

image::/assets/images/2021/09/01/ltc-tools-whatsin-package.cast.svg[ltc-tools-whatsin-package.cast]

We have

[source,bash]
----
jltc2mtc
jltcdump
jltcgen
jltcntp
jltctrigger
ltcdump
ltcgen

----

Let's see what each of them does.

== Using ltc-tools

=== jltc2mtc: JACK app to translate linear time code to midi time code.

[source,bash]
----
jltc2mtc --help
jltc2mtc - JACK app to translate linear time code to midi time code.

Usage: jltc2mtc [ OPTIONS ]

Options:
  -f, --fps &amp;lt;num&amp;gt;[/den]      set expected [initial] framerate (default 25/1)
  -F, --detectfps            autodetect framerate from LTC (recommended)
  -l, --ltcport &amp;lt;portname&amp;gt;   autoconnect LTC input port
  -m, --mtcport &amp;lt;portname&amp;gt;   autoconnect MTC output port
  -s, --sysex                send system-excluve seek message
                             instead of MTC quarter frames
  -h, --help                 display this help and exit
  -V, --version              print version information and exit


This tool reads LTC from a JACK-audio port and generates corresponding
MTC on a JACK-midi port.

jltc2mtc supports both forward and backwards played timecode, and compensates
for decoder and port latencies.
Note that MTC only supports 4 framerates: 24, 25, 30df and 30 fps.
Framerates other than that are announced as 25fps MTC.
Drop-frame-timecode is detected by the corresponding bit in the LTC frame,
regardless of the -F option. You can /force/ it with -f 30000/1001.

Note that MTC distinguishes between film speed and video speed only by the
rate at which timecode advances, not by the information contained in the
timecode messages; thus, 29.97 fps dropframe is represented as 30 fps
dropframe with 0.1% pulldown

----

Nice utils, but that's not what I'm looking for. Next!

=== jltcdump: JACK app to parse linear time code.

[source,bash]
----
jltcdump --help
jltcdump - JACK app to parse linear time code.

Usage: jltcdump [ OPTIONS ] [ JACK-PORTS ]

Options:
  -f, --fps  &amp;lt;num&amp;gt;[/den]     set expected [initial] framerate (default 25/1)
  -F, --detectfps            autodetect framerate from LTC
  -H  &amp;lt;alpha&amp;gt;
  --highpass &amp;lt;alpha&amp;gt;         set R/S highpass filter coefficient (dflt 0.6)
  -h, --help                 display this help and exit
  -o, --output &amp;lt;path&amp;gt;        write to file(s)
  -s, --signals              start/stop parser using SIGUSR1/SIGUSR2
  -r, --runstop              parse R/S signal on 2nd channel
  -R  &amp;lt;float&amp;gt;,
  --rsthreshold &amp;lt;float&amp;gt;      R/S signal threshold (default 0.01)
  -V, --version              print version information and exit


If both -s and -o are given, &amp;lt;path&amp;gt; is used a prefix:
The filename will be &amp;lt;path&amp;gt;YYMMDD-HHMMSS.tme.XXXXX .
If only -o is set, &amp;lt;path&amp;gt; is as filename.

In 'signal' mode, the application starts in 'idle' state
and won't record LTC until it receives SIGUSR1.

The fps option is only needed to properly track the first LTC frame,
and timecode discontinuity notification.
The LTC-decoder detects and tracks the speed but it takes a few samples
to establish initial synchronization. Setting fps to the expected fps
speeds up the initial sync process. The default is 25/1.

----

Pretty cool, but this is working at the other end of the spectrum, so to speak. I need for the time being to generate timestamps, not yet to read them through the audio input.

=== ltcgen: JACK audio client to generate linear time code in realtime.

[source,bash]
----
jltcgen --help
ltcgen - JACK audio client to generate linear time code in realtime.
Usage: jltcgen [OPTION] [JACK-PORT-TO-CONNECT]*

Options:
 -d, --date datestring      set date, format is either DDMMYY or MM/DD/YY
 -f, --fps fps              set frame-rate NUM[/DEN][ndf|df] default: 25/1ndf
 -h, --help                 display this help and exit
 -g, --volume float         set output level in dBFS default -18db
 -l, --localtime            when using current time, do it in local TZ (not UTC)
 -m, --timezone tz          set timezone in minutes-west of UTC
 -r, --auto-resync          automatically resync if drift is more than 100ms
 -t, --timecode time        specify start-time/timecode [[[HH:]MM:]SS:]FF
 -u, --userbits bcd         specify fixed BCD user bits (max. 8 BCD digits)
                            CAUTION: This ignores any date/timezone settings!
 -w, --wait                 wait for a key-stroke before starting.
 -V, --version              print version information and exit
 -z, --timezone tz          set timezone +HHMM

Unless a timecode (-t) is given, the current time/date are used.
Date (-d) and timezone (-z, -m) are only used if a timecode is given.
The timezome may be specified either as HHMM zone, or in minutes-west of UTC.

SIGINT (CTRL+C) prints current clock-drift (audio-clock - system-clock).
SIGQUIT (CTRL+\) terminates the program.
SIGHUP initialize a re-sync to system clock (unless -t is given).

----

Yes, that's what I was looking for! Spoiler alert: it just won't work on my tiny machine because I haven't yet installed Jack, and because I haven't added the official Zero audio link:https://fr.aliexpress.com/item/32770665186.html?spm=a2g0o.store_pc_groupList.8148356.1.7f667fddpaPeIL[hat] yet on this machine...
I will have to give it another try once I get everything installed (where the hell did I "store" that audio hat?).

[source,bash]
----
jltcgen
Cannot connect to server socket err = No such file or directory
Cannot connect to server request channel
exec of JACK server (command = "/usr/bin/jackd") failed: No such file or directory
Cannot connect to server socket err = No such file or directory

----

You know what, the goal of this blog post was to install libltc, and we already succeeded, so let's call it a day, or close let's just explore the rest of the binaries before going to bed.

=== jltcntp: JACK LTC parser with NTP SHM support.

----
jltcntp --help
jltcntp - JACK LTC parser with NTP SHM support

Usage: jltcntp [ options ] [ JACK-ports ]

Options:
  -f, --fps  &amp;lt;num&amp;gt;[/den]     set expected framerate (default 25/1)
  -u, --unit &amp;lt;u&amp;gt;             send LTC to NTP SHM driver unit &amp;lt;u&amp;gt; (default none)
  -n, --no-date              ignore date received via LTC
  -v, --verbose              output data to stdout
  -h, --help                 display this help and exit
  -V, --version              print version information and exit


----

SHM: _shared memory driver_

NTP: _Network Time Protocol_

Not (yet?) what I'm looking for...

=== jltctrigger: JACK app to trigger actions on given LTC.

[source,bash]
----
jltctrigger --help
jltctrigger - JACK app to trigger actions on given LTC.

Usage: jltctrigger [ OPTIONS ] &amp;lt;cfg-file&amp;gt; ...

Options:
  -c, --connect &amp;lt;port&amp;gt;       auto-connect to given jack-port
  -f, --fps &amp;lt;num&amp;gt;[/den]      set expected [initial] framerate (default 25/1)
  -F, --detectfps            autodetect framerate from LTC
  -h, --help                 display this help and exit
  -p, --print                output decoded LTC (live)
  -v, --verbose              be verbose
  -V, --version              print version information and exit



Actions are defined in a config file, one per line.
  Timecode &amp;lt;Space&amp;gt; Command
Multiple config files can be given.
The fps parameter is used when parsing the config file,
...
The fps option is also used properly track the first LTC frame,
and timecode discontinuity notification.
The LTC-decoder detects and tracks the speed but it takes a few samples
to establish initial synchronization. Setting fps to the expected fps
speeds up the initial sync process. The default is 25/1.

----

Wow, pretty cool, you can set up an action where "hearing" a specific timestamp... What about getting a `crontab` this way? ;-) Okay, that would work only once...

=== ltcdump: parse linear time code from a audio-file.

[source,bash]
----
ltcdump --help
ltcdump - parse linear time code from a audio-file.

Usage: ltcdump [ OPTIONS ] &amp;lt;filename&amp;gt;

Options:
  -a                         write audacity label file-format
  -c, --channel &amp;lt;num&amp;gt;        decode LTC from given audio-channel (first = 1)
  -d, --decodedate           decode date from LTC frame
  -f, --fps  &amp;lt;num&amp;gt;[/den]     set expected [initial] framerate
  -F, --detectfps            autodetect framerate from LTC (recommended)
  -h, --help                 display this help and exit
  -V, --version              print version information and exit


Channel count starts at '1', which is also the default channel to analyze.

The fps option is only needed to properly track the first LTC frame,
and timecode discontinuity notification.
The LTC-decoder detects and tracks the speed but it takes a few samples
to establish initial synchronization. Setting fps to the expected fps
speeds up the initial sync process. The default is 25/1.

----

As I'm not yet able to generate analog audio with the Zero, that could prove handy if I find another util that could generate an LTC audio file. Could that be the very last util?

=== ltcgen: generate linear time code audio-file.

Yes, we got this!

[source,bash]
----
ltcgen --help
ltcgen - generate linear time code audio-file.
Usage: ltcgen [OPTION] &amp;lt;output-file&amp;gt;

Options:
 -d, --date datestring      set date, format is either DDMMYY or MM/DD/YY
 -f, --fps fps              set frame-rate NUM[/DEN][ndf|df] default: 25/1ndf
 -g, --volume float         set output level in dBFS default -18db
 -h, --help                 display this help and exit
 -l, --duration time        set duration of file to encode [[[HH:]MM:]SS:]FF.
 -m, --timezone tz          set timezone in minutes-west of UTC
 -r, --reverse              encode backwards from start-time
 -s, --samplerate sr        specify samplerate (default 48000)
 -t, --timecode time        specify start-time/timecode [[[HH:]MM:]SS:]FF
 -u, --userbits bcd         specify fixed BCD user bits (max. 8 BCD digits)
                            CAUTION: This ignores any date/timezone settings!
 -V, --version              print version information and exit
 -z, --timezone tz          set timezone +HHMM

Unless a timecode (-t) is given, the current time/date are used.
Date (-d) and timezone (-z, -m) are only used if a timecode is given.
The timezome may be specified either as HHMM zone, or in minutes-west of UTC.

If the duration is &amp;lt;=0, ltcgen write until it receives SIGINT.

The output file-format is WAV, signed 16 bit, mono.

----

Let's see if my machine has an almost correct time, despite not having any RTC yet:

[source,bash]
----
date
lundi 6 septembre 2021, 18:50:33 (UTC+0200)

----

My PC is at the same time as the Zero, so all is good (thank you NTP I guess).

[source,bash]
----
ltcgen timecoded-audio.wav
writing to 'timecoded-audio.wav'
samplerate: 48000, duration 60000.0 ms
cfg LTC:   06/09/21 (DD/MM/YY) 16:52:34:03 +0000
wrote 2880192 audio-samples

----

Let's decode the encoded audio now:

image::/assets/images/2021/09/01/ltc-tools-decode-audio.cast.svg[ltc-tools-decode-audio.cast]

[source,bash]
----
ltcdump timecoded-audio.wav |more
ltcdump timecoded-audio.wav |tail

----

We have one second of encoded timestamps. Nice!

Want to hear it? link:/assets/sounds/2021/09/07/timecoded-audio.wav[Here] you go! Yes, you've heard better music in the past.

== Bonus? Packaging the official libltc Debian version, but for armhf

Should you make any change to the source code, it would come in handy to rebuild the Debian package. In order to build a libltc Debian package from source, we need to install necessary build tools:

`debhelper dpkg-dev fakeroot`

To install what's necessary, do

[source,bash]
----
sudo apt install debhelper dpkg-dev fakeroot

----

=== Enable source repositories

Make sure you have enabled the source repositories.

To do so, open _/etc/apt/sources.list_ file:

[source,bash]
----
sudo vi /etc/apt/sources.list

----

You will see some lines in it like below:

[source,bash]
----
deb http://deb.debian.org/debian buster main contrib non-free
#deb-src http://deb.debian.org/debian buster main contrib non-free
deb http://deb.debian.org/debian buster-updates main contrib non-free
#deb-src http://deb.debian.org/debian buster-updates main contrib non-free
[...]

----

In the above file, all the lines beginning with one or two hashes (#) are just comments, for information only. And the lines without hashes are the apt repositories.
Uncomment the `"deb-src` lines by removing the hashes. Then, issue a `sudo apt update`.

=== Install build dependencies for the package

Next, we need to install the dependencies for that package as well. To do so, run:

[source,bash]
----
sudo apt build-dep libltc11

----

image::/assets/images/2021/09/01/ltc-build-dep.cast.svg[ltc-build-dep.cast]

=== Download source packages

Download the source of the libltc package:

[source,bash]
----
apt source libltc

----

image::/assets/images/2021/09/01/ltc-download-package-source.cast.svg[ltc-download-package-source.cast]

=== Build the package

[source,bash]
----
cd libltc-1.3.0/
dpkg-buildpackage -rfakeroot -b -uc -us
ll ../libltc*deb

----

image::/assets/images/2021/09/01/ltc-build-package-source.cast.svg[ltc-build-package-source.cast]

[source,bash]
----
-rw-r--r-- 1 poddingue poddingue 12940 sept.  6 22:20 ../libltc11_1.3.0-1_armhf.deb
-rw-r--r-- 1 poddingue poddingue 22020 sept.  6 22:20 ../libltc11-dbgsym_1.3.0-1_armhf.deb
-rw-r--r-- 1 poddingue poddingue 13420 sept.  6 22:20 ../libltc-dev_1.3.0-1_armhf.deb
-rw-r--r-- 1 poddingue poddingue 14468 sept.  6 22:20 ../libltc-doc_1.3.0-1_all.deb

----
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>armhf</category>
      <category>sound</category>
      <category>time</category>
    </item>
    <item>
      <title>termtosvg on aarch64 Debian</title>
      <dc:creator>Bruno Verachten</dc:creator>
      <pubDate>Wed, 22 Apr 2026 07:44:08 +0000</pubDate>
      <link>https://dev.to/gounthar/termtosvg-on-aarch64-debian-elj</link>
      <guid>https://dev.to/gounthar/termtosvg-on-aarch64-debian-elj</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on August 20, 2021 at &lt;a href="https://bruno.verachten.fr/2021/08/20/installing-termtosvg-on-aarch64/" rel="noopener noreferrer"&gt;https://bruno.verachten.fr/2021/08/20/installing-termtosvg-on-aarch64/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h1&gt;
  
  
  Installing termtosvg on an &lt;a href="https://en.wikipedia.org/wiki/AArch64" rel="noopener noreferrer"&gt;aarch64&lt;/a&gt; machine running &lt;a href="https://www.debian.org/releases/bullseye/" rel="noopener noreferrer"&gt;bullseye&lt;/a&gt;.
&lt;/h1&gt;

&lt;h1&gt;
  
  
  What is termtosvg and what can I do with it?
&lt;/h1&gt;

&lt;h2&gt;
  
  
  Term to what?
&lt;/h2&gt;

&lt;p&gt;termtosvg is an &lt;a href="https://github.com/nbedos/termtosvg" rel="noopener noreferrer"&gt;abandonned&lt;/a&gt; utility that allows to record terminal sessions as standalone SVG animations. Those animations come pretty handy if you want to spice up your documentation. Up to now it wasn’t available as a package in Debian, so we had to build it on the machine. Fortunately, with recent and top notch distros like &lt;a href="https://www.armbian.com/station-p1/" rel="noopener noreferrer"&gt;Armbian&lt;/a&gt;, we can install it with a simple&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;``` sudo apt install termtosvg----&lt;br&gt;
You may already know how much I love Armbian, and how much I love the link:&lt;a href="http://bruno.verachten.fr/2021/02/18/How-to-Install-Armbian-on-the-Station-P1/%5BStation" rel="noopener noreferrer"&gt;http://bruno.verachten.fr/2021/02/18/How-to-Install-Armbian-on-the-Station-P1/[Station&lt;/a&gt; P1], so the combination of the two is just like heaven on earth.&lt;/p&gt;

&lt;p&gt;image::/assets/images/2021/08/20/kardashian-sassy.gif[kardashian-sassy]&lt;/p&gt;

&lt;p&gt;I already had written a blog post for termtosvg for the OrangePi link:/2020/01/03/Terminal-recording-on-the-OrangePi-Zero/[Zero].&lt;/p&gt;

&lt;p&gt;image::/assets/images/2021/08/20/whatever-name-I-choose.svg[whatever-name-I-choose]&lt;/p&gt;

&lt;p&gt;=== How to record my terminal&lt;/p&gt;

&lt;p&gt;The animated image just on top of this text has been generated thanks to two commands, the first one being:&lt;/p&gt;

&lt;p&gt;----bash&lt;/p&gt;

&lt;h2&gt;
  
  
  termtosvg record -g 200x60 whatever-name-I-choose.cast
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;-g&lt;/code&gt; is there to specify the geometry of your target terminal. The &lt;code&gt;record&lt;/code&gt;part is there to tell you want to get a &lt;code&gt;.cast&lt;/code&gt; file at the end. That's not mandatory, you could get your command as simple as &lt;code&gt;termtosvg&lt;/code&gt;  and get the resulting SVG file as soon as you enter the &lt;code&gt;exit()&lt;/code&gt; command. We'll see in a few paragraphs why I want to generate an intermediary &lt;code&gt;.cast&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Pretty cool, heh?&lt;/p&gt;

&lt;p&gt;Okay, my terminal settings are not that fancy, so let's throw glitter in your eyes.&lt;/p&gt;

&lt;p&gt;image::/assets/images/2021/08/20/glitter.svg[glitter]&lt;/p&gt;

&lt;p&gt;Now we're talking! This "cast" has been generated with the very same command. So the command once you enter &lt;code&gt;exit()&lt;/code&gt;will create a &lt;code&gt;.cast&lt;/code&gt; file which will then be used to generate a SVG file (with &lt;code&gt;termtosvg&lt;/code&gt;) or a GIF file with the &lt;code&gt;asciicast2gif&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;=== How to generate the resulting SVG file&lt;/p&gt;

&lt;p&gt;Next mandatory step: generate the SVG file. Commands do not come any simpler:&lt;/p&gt;

&lt;p&gt;----termtosvg render glitter.cast glitter.svg----&lt;/p&gt;

&lt;p&gt;Afterwards, you just have to move your file wherever you need it and link/import it into your documentation.&lt;br&gt;
You're welcome.&lt;/p&gt;

&lt;p&gt;Now, why the hell did I generate an intermediary &lt;code&gt;.cast&lt;/code&gt;file? Well, that format can be used as an input for another project, link:&lt;a href="https://github.com/asciinema/asciicast2gif%5Basciicast2gif%5D" rel="noopener noreferrer"&gt;https://github.com/asciinema/asciicast2gif[asciicast2gif]&lt;/a&gt;, which is part of link:&lt;a href="https://github.com/asciinema/asciinema%5Basciinema%5D" rel="noopener noreferrer"&gt;https://github.com/asciinema/asciinema[asciinema]&lt;/a&gt;. Asciinema uses the same &lt;code&gt;.cast&lt;/code&gt; format as termtosvg, so we can generate SVG from asciinema &lt;code&gt;cast&lt;/code&gt; files, GIF files from termtosvg &lt;code&gt;cast&lt;/code&gt;files, and the other way around.&lt;/p&gt;

&lt;p&gt;Now, what if you're out of luck, and termtosvg is not available on your distro? Let's try to build it by ourselves:&lt;/p&gt;

&lt;p&gt;== Compile and install &lt;code&gt;termtosvg&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;----bash&lt;br&gt;
git clone &lt;a href="https://github.com/nbedos/termtosvg.git" rel="noopener noreferrer"&gt;https://github.com/nbedos/termtosvg.git&lt;/a&gt; &amp;amp;&amp;amp; cd termtosvg/&lt;br&gt;
== Prerequisites, use apt, apt-get, yum, whatever package manager you have on your system&lt;br&gt;
sudo apt-get install -y libxml2-dev libxslt-dev python3 python3-dev python3-pip python3-setuptools&lt;br&gt;
make build&lt;br&gt;
== If you ever have an error saying that python is not available, you could make an horrible hack like&lt;br&gt;
== sudo ln -s /usr/bin/python3 /usr/bin/python or anything smarter&lt;br&gt;
== Now, let's install it. Note that 1.1.0-py3 is the version that was available at the time I wrote this blog post, your&lt;br&gt;
== mileage may vary&lt;br&gt;
pip3 install dist/termtosvg-1.1.0-py3-none-any.whl&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


&amp;lt;figure&amp;gt;
&amp;lt;img src="https://bruno.verachten.fr/assets/images/2021/08/20/great%20success.gif" alt="great success" /&amp;gt;
&amp;lt;/figure&amp;gt;

Now it’s your turn to produce good looking shell recordings with termtosvg.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>aarch64</category>
      <category>armbian</category>
      <category>firefly</category>
      <category>howto</category>
    </item>
    <item>
      <title>Using jenkins to build Android applications</title>
      <dc:creator>Bruno Verachten</dc:creator>
      <pubDate>Wed, 22 Apr 2026 07:44:05 +0000</pubDate>
      <link>https://dev.to/gounthar/using-jenkins-to-build-android-applications-3lb5</link>
      <guid>https://dev.to/gounthar/using-jenkins-to-build-android-applications-3lb5</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on August 2, 2022 at &lt;a href="https://bruno.verachten.fr/2022/08/02/naively-building-android-apps-with-jenkins/" rel="noopener noreferrer"&gt;https://bruno.verachten.fr/2022/08/02/naively-building-android-apps-with-jenkins/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Come join me at &lt;a href="https://events.devopsworld.com/widget/cloudbees/devopsworld22/conferenceSessionDetails?tab.day=20220928" rel="noopener noreferrer"&gt;DevOps World 2022&lt;/a&gt; for &lt;em&gt;"Naively Building Android Apps with Jenkins. Not Natively, Naively."&lt;/em&gt;, a talk about my journey with Jenkins while trying to build and publish Android applications without prior Jenkins knowledge.&lt;/p&gt;

&lt;p&gt;I’ve been tinkering with various CI/CD tools for years (Gitlab CI, Circle CI, Travis CI, Shippable, Github Actions, …​) but not with Jenkins for whatever reason.&lt;/p&gt;

&lt;p&gt;I’ve been supporting since 2014 mobile application developers through the use of gitlab-ci.&lt;/p&gt;

&lt;p&gt;For DevOpsWorld 2022, I’ve been given a challenge: what if I tried to replicate that well oiled gitlab-centric machinery that I had built for years this time with Jenkins? At least…​ Could I mimic a subset of the functionality of my previous work without knowing anything about Jenkins?&lt;/p&gt;

&lt;p&gt;To spice things up a bit, I might as well refrain from looking at what has already been done elsewhere; thus, I may end up with a shaky solution, or even a functional system that can be replicated at will.&lt;/p&gt;

&lt;p&gt;Topics will include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;What does one need to build an Android application?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When your only tool is a hammer, everything looks like a nail (an ode to containerization)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Credentials and authentication&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;From Docker to the Cloud?&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Quality Assurance, Tests, and Deployment&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2022%2F08%2F02%2FBuildingAndroidAppsNaively.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2022%2F08%2F02%2FBuildingAndroidAppsNaively.png" alt="First slide" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This is not a definitive guide to build an Android application with Jenkins, more like a roadmap to my journey with Jenkins…​ with takeaways from my experience.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Come join me for the presentation in Orlando!&lt;/p&gt;

</description>
      <category>android</category>
      <category>ci</category>
      <category>docker</category>
      <category>jenkins</category>
    </item>
    <item>
      <title>Create a new Jenkins node, and run your Jenkins agent as a service</title>
      <dc:creator>Bruno Verachten</dc:creator>
      <pubDate>Wed, 22 Apr 2026 07:44:03 +0000</pubDate>
      <link>https://dev.to/gounthar/create-a-new-jenkins-node-and-run-your-jenkins-agent-as-a-service-47ak</link>
      <guid>https://dev.to/gounthar/create-a-new-jenkins-node-and-run-your-jenkins-agent-as-a-service-47ak</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on August 2, 2022 at &lt;a href="https://bruno.verachten.fr/2022/08/02/run-your-jenkins-agent-as-a-service/" rel="noopener noreferrer"&gt;https://bruno.verachten.fr/2022/08/02/run-your-jenkins-agent-as-a-service/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;In this tutorial, we will see how to start a Jenkins agent as a Linux service with &lt;code&gt;systemd&lt;/code&gt;. Most of the time, I use &lt;code&gt;Docker&lt;/code&gt; for my agents, and I enter the right options on the command line so that they restart automatically. But sometimes, especially when you want to use the famous &lt;a href="https://www.jenkins.io/doc/book/pipeline/docker/" rel="noopener noreferrer"&gt;&lt;code&gt;Dockerfile: true&lt;/code&gt;&lt;/a&gt; option, you need to start the agent manually with a &lt;code&gt;java&lt;/code&gt; command and not with Docker (for various security reasons). And then you need to restart it manually if you have to reboot, or if you forget to use &lt;code&gt;nohup&lt;/code&gt; to start it in the background and then close the terminal.&lt;/p&gt;

&lt;h1&gt;
  
  
  Prerequisites
&lt;/h1&gt;

&lt;p&gt;Let’s say we’re starting with a fresh Ubuntu 22.04 Linux installation. What do we need to get an agent working? Java, that’s for sure, but also Docker if we want to use Docker for our agents instead of installing everything directly on the machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Java
&lt;/h2&gt;

&lt;p&gt;Nowadays, openjdk 11 is recommended, and openjdk 17 is supported. Let’s go with openjdk 17:&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="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;--no-install-recommends&lt;/span&gt; openjdk-17-jdk-headless
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s now verify if java works for us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;java &lt;span class="nt"&gt;-version&lt;/span&gt;
openjdk version &lt;span class="s2"&gt;"17.0.3"&lt;/span&gt; 2022-04-19
OpenJDK Runtime Environment &lt;span class="o"&gt;(&lt;/span&gt;build 17.0.3+7-Ubuntu-0ubuntu0.22.04.1&lt;span class="o"&gt;)&lt;/span&gt;
OpenJDK 64-Bit Server VM &lt;span class="o"&gt;(&lt;/span&gt;build 17.0.3+7-Ubuntu-0ubuntu0.22.04.1, mixed mode, sharing&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Jenkins user
&lt;/h2&gt;

&lt;p&gt;As we’re creating an agent, we’d better separate rights, permissions, and ownership. Let’s create a user for Jenkins:&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="nb"&gt;sudo &lt;/span&gt;adduser &lt;span class="nt"&gt;--group&lt;/span&gt; &lt;span class="nt"&gt;--home&lt;/span&gt; /home/jenkins &lt;span class="nt"&gt;--shell&lt;/span&gt; /bin/bash jenkins
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Docker
&lt;/h2&gt;

&lt;p&gt;Now, to get a recent version of Docker, we should install the &lt;code&gt;docker-ce&lt;/code&gt; package and a few others with a particular repo. First, let’s add the needed dependencies to add the repo:&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="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;ca-certificates curl gnupg lsb-release
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my case, these packages were already installed and up to date. The next step is to add Docker’s official GPG key:&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="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /etc/apt/keyrings
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://download.docker.com/linux/ubuntu/gpg | &lt;span class="nb"&gt;sudo &lt;/span&gt;gpg &lt;span class="nt"&gt;--dearmor&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /etc/apt/keyrings/docker.gpg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we can set up the repo:&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="nb"&gt;echo&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"deb [arch=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;dpkg &lt;span class="nt"&gt;--print-architecture&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
  &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;lsb_release &lt;span class="nt"&gt;-cs&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt; stable"&lt;/span&gt; | &lt;span class="nb"&gt;sudo tee&lt;/span&gt; /etc/apt/sources.list.d/docker.list &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last thing to do is to update the list of available packages, and then install the latest version of Docker:&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="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;docker-ce docker-ce-cli containerd.io docker-compose-plugin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you’re - like me - running a recent version of Ubuntu, you won’t need to create the &lt;code&gt;docker&lt;/code&gt; group, because it has been created with the installation of Docker. On the contrary, you can then issue a &lt;code&gt;sudo groupadd docker&lt;/code&gt; command to create the &lt;code&gt;docker&lt;/code&gt; group.&lt;/p&gt;

&lt;p&gt;Now, let’s add our current user to the &lt;code&gt;docker&lt;/code&gt; group:&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="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker &lt;span class="nv"&gt;$USER&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And if you’re not using the default user, but &lt;code&gt;jenkins&lt;/code&gt;, you can do the same:&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="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; docker jenkins
&lt;span class="nb"&gt;sudo &lt;/span&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; &lt;span class="nb"&gt;sudo &lt;/span&gt;jenkins
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now log out, and log back in so that your group membership is updated. If you get any error, just reboot the machine, this sometimes happens. &lt;code&gt;¯_(ツ)_/¯&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Mandatory "Hello World!" Docker installation test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run hello-world
Unable to find image &lt;span class="s1"&gt;'hello-world:latest'&lt;/span&gt; locally
latest: Pulling from library/hello-world
2db29710123e: Pull &lt;span class="nb"&gt;complete
&lt;/span&gt;Digest: sha256:53f1bbee2f52c39e41682ee1d388285290c5c8a76cc92b42687eecf38e0af3f0
Status: Downloaded newer image &lt;span class="k"&gt;for &lt;/span&gt;hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 &lt;span class="nb"&gt;.&lt;/span&gt; The Docker client contacted the Docker daemon.
 &lt;span class="nb"&gt;.&lt;/span&gt; The Docker daemon pulled the &lt;span class="s2"&gt;"hello-world"&lt;/span&gt; image from the Docker Hub.
    &lt;span class="o"&gt;(&lt;/span&gt;amd64&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="nb"&gt;.&lt;/span&gt; The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 &lt;span class="nb"&gt;.&lt;/span&gt; The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 &lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-it&lt;/span&gt; ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nice!&lt;/p&gt;

&lt;h1&gt;
  
  
  Create a new node in Jenkins
&lt;/h1&gt;

&lt;p&gt;Quoting the official &lt;a href="https://www.jenkins.io/doc/book/managing/nodes/" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;,&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nodes are the "machines" on which build agents run.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and also:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Agents manage the task execution on behalf of the Jenkins controller by using executors. An agent is actually a small (170KB single jar) Java client process that connects to a Jenkins controller and is assumed to be unreliable. An agent can use any operating system that supports Java. Tools required for builds and tests are installed on the node where the agent runs; they can be installed directly or in a container (Docker or Kubernetes).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To conclude:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In practice, nodes and agents are essentially the same but it is good to remember that they are conceptually distinct.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;We will now create a new node in Jenkins, using our Ubuntu machine as the node, and then launch an agent on this node.&lt;/p&gt;

&lt;h2&gt;
  
  
  Node creation in the UI
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Go to your Jenkins dashboard&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go to &lt;em&gt;Manage Jenkins&lt;/em&gt; option in the main menu&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Go to &lt;em&gt;Manage Nodes and clouds&lt;/em&gt; item&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2022%2F08%2F03%2Fmanage-nodes-and-clouds.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2022%2F08%2F03%2Fmanage-nodes-and-clouds.png" alt="Manage Nodes and Clouds" width="800" height="274"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Go to New Node option in the side menu&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Fill in the &lt;em&gt;Node name&lt;/em&gt; (&lt;em&gt;My New Ubuntu 22.04 Node with Java and Docker installed&lt;/em&gt; for me) and type (&lt;em&gt;Permanent Agent&lt;/em&gt; for me)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2022%2F08%2F03%2Fnew-node.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2022%2F08%2F03%2Fnew-node.png" alt="New node" width="800" height="271"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Click on the &lt;em&gt;Create&lt;/em&gt; button&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the &lt;em&gt;Description&lt;/em&gt; field, enter if you want a human-readable description of the node (&lt;em&gt;My New Ubuntu 22.04 Node with Java and Docker installed&lt;/em&gt; for me)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Let &lt;code&gt;1&lt;/code&gt; as the number of executors for the time being. A good value to start with would be the number of CPU cores on the machine (unfortunately for me, it’s &lt;code&gt;1&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;As &lt;em&gt;Remote root directory&lt;/em&gt;, enter the directory where you want to install the agent (&lt;code&gt;/home/jenkins&lt;/code&gt; for me)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;An agent should have a directory dedicated to Jenkins. It is best to use an absolute path, such as &lt;code&gt;/var/jenkins&lt;/code&gt; or &lt;code&gt;c:\jenkins&lt;/code&gt;. This should be a path local to the agent machine. There is no need for this path to be visible from the controller.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Regarding the &lt;em&gt;Labels&lt;/em&gt; field, enter the labels you want to assign to the node (&lt;code&gt;ubuntu linux docker jdk17&lt;/code&gt; for me, which makes four labels. This will help you group multiple agents into one logical group)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;For the &lt;em&gt;Usage&lt;/em&gt; now, choose &lt;em&gt;Use this node as much as possible&lt;/em&gt; for the time being, you will be able to restrict later on the kind of jobs that can be run on this node.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The last thing to set up now: choose &lt;em&gt;Launch agent by connecting it to the controller&lt;/em&gt; . That means that you will have to launch the agent on the node itself and that the agent will then connect to the controller. That’s pretty handy when you want to build Docker images, or when your process will use Docker images… You could also have the controller launch an agent directly via Docker remotely, but then you would have to use Docker in Docker, which is complicated &lt;em&gt;and&lt;/em&gt; insecure.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Node configuration
&lt;/h2&gt;

&lt;p&gt;The &lt;em&gt;Save&lt;/em&gt; button will create the node within Jenkins, and lead you to the &lt;em&gt;Manage nodes and clouds&lt;/em&gt; page. Your new node will appear &lt;em&gt;brown&lt;/em&gt; in the list, and you can click on it to see its details. The details page displays your java command line to start the agent. image::/assets/images/2022/08/03/java-command-to-launch-the-agent.png[Command to launch the agent]&lt;/p&gt;

&lt;p&gt;This command looks like that for me:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sO&lt;/span&gt; http://my_ip:8080/jnlpJars/agent.jar
java &lt;span class="nt"&gt;-jar&lt;/span&gt; agent.jar &lt;span class="nt"&gt;-jnlpUrl&lt;/span&gt; http://my_ip:8080/computer/My%20New%20Ubuntu%2022%2E04%20Node%20with%20Java%20and%20Docker%20installed/jenkins-agent.jnlp &lt;span class="nt"&gt;-secret&lt;/span&gt; my_secret &lt;span class="nt"&gt;-workDir&lt;/span&gt; &lt;span class="s2"&gt;"/home/jenkins"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2022%2F08%2F03%2Fcurl-and-java-launch-agent.svg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2022%2F08%2F03%2Fcurl-and-java-launch-agent.svg" alt="New agent starting" width="1600" height="1022.0"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can now go back into Jenkins' UI, click on the &lt;em&gt;Back to List&lt;/em&gt; menu item on the left and see that your new agent is doing fine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2022%2F08%2F03%2Fnew-node-looks-fine.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fbruno.verachten.fr%2Fassets%2Fimages%2F2022%2F08%2F03%2Fnew-node-looks-fine.png" alt="New node looks fine" width="800" height="33"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now what? Are we done yet? I’m afraid we’re not. Whenever you close the terminal you launched the agent with, the agent will stop. If you ever have to reboot the machine after a kernel update, you will have to restart the agent manually too. Therefore, I suggest you keep the agent running by declaring it as a service.&lt;/p&gt;

&lt;h1&gt;
  
  
  Run your Jenkins agent as a service
&lt;/h1&gt;

&lt;p&gt;Create a directory called &lt;code&gt;jenkins&lt;/code&gt; or &lt;code&gt;jenkins-service&lt;/code&gt; in your home directory or anywhere else where you have access. It could be in &lt;code&gt;/usr/local/jenkins-service&lt;/code&gt; for example. If the new directory does not belong to the current user home, give it the right owner and group after creation. For me, it would look like the following:&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="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /usr/local/jenkins-service
&lt;span class="nb"&gt;sudo chown &lt;/span&gt;jenkins /usr/local/jenkins-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Move the &lt;code&gt;agent.jar&lt;/code&gt; file that you downloaded earlier with the &lt;code&gt;curl&lt;/code&gt; command to this directory.&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="nb"&gt;mv &lt;/span&gt;agent.jar /usr/local/jenkins-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now (in &lt;code&gt;/usr/local/jenkins-service&lt;/code&gt;) create a &lt;code&gt;start-agent.sh&lt;/code&gt; file with the Jenkins &lt;code&gt;java&lt;/code&gt; command we’ve seen earlier as the file’s content.&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="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /usr/local/jenkins-service
&lt;span class="o"&gt;==&lt;/span&gt; Just &lt;span class="k"&gt;in case&lt;/span&gt; we would have upgraded the controller, we need to make sure that the agent is using the latest version of the agent.jar
curl &lt;span class="nt"&gt;-sO&lt;/span&gt; http://my_ip:8080/jnlpJars/agent.jar
java &lt;span class="nt"&gt;-jar&lt;/span&gt; agent.jar &lt;span class="nt"&gt;-jnlpUrl&lt;/span&gt; http://my_ip:8080/computer/My%20New%20Ubuntu%2022%2E04%20Node%20with%20Java%20and%20Docker%20installed/jenkins-agent.jnlp &lt;span class="nt"&gt;-secret&lt;/span&gt; my_secret &lt;span class="nt"&gt;-workDir&lt;/span&gt; &lt;span class="s2"&gt;"/home/jenkins"&lt;/span&gt;
&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make the script executable by executing &lt;code&gt;chmod +x start-agent.sh&lt;/code&gt; in the directory.&lt;/p&gt;

&lt;p&gt;Now create a &lt;code&gt;/etc/systemd/system/jenkins-agent.service&lt;/code&gt; file with the following content:&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="o"&gt;[&lt;/span&gt;Unit]
&lt;span class="nv"&gt;Description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Jenkins Agent

&lt;span class="o"&gt;[&lt;/span&gt;Service]
&lt;span class="nv"&gt;User&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;jenkins
&lt;span class="nv"&gt;WorkingDirectory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/home/jenkins
&lt;span class="nv"&gt;ExecStart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/bin/bash /usr/local/jenkins-service/start-agent.sh
&lt;span class="nv"&gt;Restart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;always

&lt;span class="o"&gt;[&lt;/span&gt;Install]
&lt;span class="nv"&gt;WantedBy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;multi-user.target
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We still have to enable the daemon with the following command:&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="nb"&gt;sudo &lt;/span&gt;systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;jenkins-agent.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s have a look at the system logs before starting the daemon:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;journalctl &lt;span class="nt"&gt;-f&lt;/span&gt; &amp;amp;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now start the daemon with the following command.&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="nb"&gt;sudo &lt;/span&gt;systemctl start jenkins-agent.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I can see some interesting logs in the &lt;code&gt;journalctl&lt;/code&gt; output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Aug 03 19:37:27 ubuntu-machine systemd[1]: Started Jenkins Agent.
Aug 03 19:37:27 ubuntu-machine &lt;span class="nb"&gt;sudo&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;8821]: pam_unix&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt;:session&lt;span class="o"&gt;)&lt;/span&gt;: session closed &lt;span class="k"&gt;for &lt;/span&gt;user root
Aug 03 19:37:28 ubuntu-machine bash[8826]: Aug 03, 2022 7:37:28 PM org.jenkinsci.remoting.engine.WorkDirManager initializeWorkDir
Aug 03 19:37:28 ubuntu-machine bash[8826]: INFO: Using /home/jenkins/remoting as a remoting work directory
Aug 03 19:37:28 ubuntu-machine bash[8826]: Aug 03, 2022 7:37:28 PM org.jenkinsci.remoting.engine.WorkDirManager setupLogging
Aug 03 19:37:28 ubuntu-machine bash[8826]: INFO: Both error and output logs will be printed to /home/jenkins/remoting
Aug 03 19:37:28 ubuntu-machine bash[8826]: Aug 03, 2022 7:37:28 PM hudson.remoting.jnlp.Main createEngine
Aug 03 19:37:28 ubuntu-machine bash[8826]: INFO: Setting up agent: My New Ubuntu 22.04 Node with Java and Docker installed
Aug 03 19:37:28 ubuntu-machine bash[8826]: Aug 03, 2022 7:37:28 PM hudson.remoting.Engine startEngine
Aug 03 19:37:28 ubuntu-machine bash[8826]: INFO: Using Remoting version: 3046.v38db_38a_b_7a_86
Aug 03 19:37:28 ubuntu-machine bash[8826]: Aug 03, 2022 7:37:28 PM org.jenkinsci.remoting.engine.WorkDirManager initializeWorkDir
Aug 03 19:37:28 ubuntu-machine bash[8826]: INFO: Using /home/jenkins/remoting as a remoting work directory
Aug 03 19:37:29 ubuntu-machine bash[8826]: Aug 03, 2022 7:37:29 PM hudson.remoting.jnlp.Main&lt;span class="nv"&gt;$CuiListener&lt;/span&gt; status
Aug 03 19:37:29 ubuntu-machine bash[8826]: INFO: Locating server among &lt;span class="o"&gt;[&lt;/span&gt;http://controller_ip:58080/]
Aug 03 19:37:29 ubuntu-machine bash[8826]: Aug 03, 2022 7:37:29 PM org.jenkinsci.remoting.engine.JnlpAgentEndpointResolver resolve
Aug 03 19:37:29 ubuntu-machine bash[8826]: INFO: Remoting server accepts the following protocols: &lt;span class="o"&gt;[&lt;/span&gt;JNLP4-connect, Ping]
Aug 03 19:37:29 ubuntu-machine bash[8826]: Aug 03, 2022 7:37:29 PM hudson.remoting.jnlp.Main&lt;span class="nv"&gt;$CuiListener&lt;/span&gt; status
Aug 03 19:37:29 ubuntu-machine bash[8826]: INFO: Agent discovery successful
Aug 03 19:37:29 ubuntu-machine bash[8826]:   Agent address: controller_ip
Aug 03 19:37:29 ubuntu-machine bash[8826]:   Agent port:    50000
Aug 03 19:37:29 ubuntu-machine bash[8826]:   Identity:      31:c4:f9:31:46:c3:eb:72:64:a3:c7:d6:c7:ea:32:2f
Aug 03 19:37:29 ubuntu-machine bash[8826]: Aug 03, 2022 7:37:29 PM hudson.remoting.jnlp.Main&lt;span class="nv"&gt;$CuiListener&lt;/span&gt; status
Aug 03 19:37:29 ubuntu-machine bash[8826]: INFO: Handshaking
Aug 03 19:37:29 ubuntu-machine bash[8826]: Aug 03, 2022 7:37:29 PM hudson.remoting.jnlp.Main&lt;span class="nv"&gt;$CuiListener&lt;/span&gt; status
Aug 03 19:37:29 ubuntu-machine bash[8826]: INFO: Connecting to controller_ip:50000
Aug 03 19:37:29 ubuntu-machine bash[8826]: Aug 03, 2022 7:37:29 PM hudson.remoting.jnlp.Main&lt;span class="nv"&gt;$CuiListener&lt;/span&gt; status
Aug 03 19:37:29 ubuntu-machine bash[8826]: INFO: Trying protocol: JNLP4-connect
Aug 03 19:37:29 ubuntu-machine bash[8826]: Aug 03, 2022 7:37:29 PM org.jenkinsci.remoting.protocol.impl.BIONetworkLayer&lt;span class="nv"&gt;$Reader&lt;/span&gt; run
Aug 03 19:37:29 ubuntu-machine bash[8826]: INFO: Waiting &lt;span class="k"&gt;for &lt;/span&gt;ProtocolStack to start.
Aug 03 19:37:30 ubuntu-machine bash[8826]: Aug 03, 2022 7:37:30 PM hudson.remoting.jnlp.Main&lt;span class="nv"&gt;$CuiListener&lt;/span&gt; status
Aug 03 19:37:30 ubuntu-machine bash[8826]: INFO: Remote identity confirmed: 31:c4:f9:31:46:c3:eb:72:64:a3:c7:d6:c7:ea:32:2f
Aug 03 19:37:30 ubuntu-machine bash[8826]: Aug 03, 2022 7:37:30 PM hudson.remoting.jnlp.Main&lt;span class="nv"&gt;$CuiListener&lt;/span&gt; status
Aug 03 19:37:30 ubuntu-machine bash[8826]: INFO: Connected
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can now check the status with the command below and the output should be similar to what you can see below.&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="nb"&gt;sudo &lt;/span&gt;systemctl status jenkins-agent.service
● jenkins-agent.service - Jenkins Agent
     Loaded: loaded &lt;span class="o"&gt;(&lt;/span&gt;/etc/systemd/system/jenkins-agent.service&lt;span class="p"&gt;;&lt;/span&gt; enabled&lt;span class="p"&gt;;&lt;/span&gt; vendor preset: enabled&lt;span class="o"&gt;)&lt;/span&gt;
     Active: active &lt;span class="o"&gt;(&lt;/span&gt;running&lt;span class="o"&gt;)&lt;/span&gt; since Wed 2022-08-03 19:37:27 UTC&lt;span class="p"&gt;;&lt;/span&gt; 4min 0s ago
   Main PID: 8825 &lt;span class="o"&gt;(&lt;/span&gt;bash&lt;span class="o"&gt;)&lt;/span&gt;
      Tasks: 22 &lt;span class="o"&gt;(&lt;/span&gt;limit: 1080&lt;span class="o"&gt;)&lt;/span&gt;
     Memory: 63.1M
        CPU: 9.502s
     CGroup: /system.slice/jenkins-agent.service
             ├─8825 /bin/bash /usr/local/jenkins-service/start-agent.sh
             └─8826 java &lt;span class="nt"&gt;-jar&lt;/span&gt; agent.jar &lt;span class="nt"&gt;-jnlpUrl&lt;/span&gt; http://controller_ip:8080/computer/My%20New%20Ubuntu%2022%2E04%20Node%20with%20Java%20and%20Docker%20installed/jenkins-agent.jnlp &lt;span class="nt"&gt;-secret&lt;/span&gt; my_secret&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Just for fun, we can now reboot the machine and see on the UI if the agent is still running once the boot is finished.&lt;/p&gt;

</description>
      <category>ci</category>
      <category>docker</category>
      <category>java</category>
      <category>jenkins</category>
    </item>
    <item>
      <title>Build a Jenkins MSI on your own Windows machine</title>
      <dc:creator>Bruno Verachten</dc:creator>
      <pubDate>Wed, 22 Apr 2026 07:44:01 +0000</pubDate>
      <link>https://dev.to/gounthar/build-a-jenkins-msi-on-your-own-windows-machine-7m1</link>
      <guid>https://dev.to/gounthar/build-a-jenkins-msi-on-your-own-windows-machine-7m1</guid>
      <description>&lt;p&gt;&lt;em&gt;Originally published on December 1, 2022 at &lt;a href="https://bruno.verachten.fr/2022/12/01/building-the-jenkins-msi-on-your-windows-machine/" rel="noopener noreferrer"&gt;https://bruno.verachten.fr/2022/12/01/building-the-jenkins-msi-on-your-windows-machine/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;Should you ever need to rebuild a Jenkins MSI on your Windows machine, here is a way to do it.&lt;/p&gt;

&lt;h1&gt;
  
  
  Pre-requisites
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Jenkins WAR file
&lt;/h1&gt;

&lt;p&gt;First of all, you should get the Jenkins war file that will be inside that MSI file. You can get it from the official Jenkins website or from the Jenkins update center. I will use the official Jenkins website in this article.&lt;/p&gt;

&lt;p&gt;Check &lt;a href="https://www.jenkins.io/download/" rel="noopener noreferrer"&gt;Jenkins download page&lt;/a&gt; and download the &lt;a href="https://updates.jenkins.io/latest/jenkins.war" rel="noopener noreferrer"&gt;latest weekly version&lt;/a&gt; of Jenkins for example.&lt;/p&gt;

&lt;h1&gt;
  
  
  Git tool
&lt;/h1&gt;

&lt;p&gt;You have a few options here: * Install &lt;a href="https://community.chocolatey.org/packages/git" rel="noopener noreferrer"&gt;Git for Windows&lt;/a&gt; * Use your IDE to clone the repository * Use &lt;a href="https://desktop.github.com/" rel="noopener noreferrer"&gt;GitHub Desktop&lt;/a&gt; * Use &lt;a href="https://www.sourcetreeapp.com/" rel="noopener noreferrer"&gt;SourceTree&lt;/a&gt; * Use Cygwin integrated &lt;a href="https://cygwin.com/packages/summary/git.html" rel="noopener noreferrer"&gt;Git command&lt;/a&gt; * […​]&lt;/p&gt;

&lt;h1&gt;
  
  
  Install MSBuild
&lt;/h1&gt;

&lt;p&gt;You can install &lt;a href="https://aka.ms/vs/17/release/vs_BuildTools.exe" rel="noopener noreferrer"&gt;MSBuild&lt;/a&gt; from Visual Studio or from the &lt;a href="https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2022" rel="noopener noreferrer"&gt;Build Tools for Visual Studio&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This command line tool is used to build the MSI file.&lt;/p&gt;

&lt;h1&gt;
  
  
  Install .NET Framework 3.5
&lt;/h1&gt;

&lt;p&gt;You may already have it installed on your machine, but not activated. You can activate it from the Windows Features dialog box.&lt;/p&gt;

&lt;p&gt;To access this dialog box, smash the &lt;code&gt;WinKey&lt;/code&gt; plus &lt;code&gt;R&lt;/code&gt;, then enter the command &lt;code&gt;appwiz.cpl&lt;/code&gt; and push enter. Search for&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Turn Windows features on or off.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Tick the &lt;code&gt;.NET Framework 3.5&lt;/code&gt; entry and install.&lt;/p&gt;

&lt;p&gt;Important: now run Windows Update to check for security updates.&lt;/p&gt;

&lt;p&gt;If it is not installed yet, you can install &lt;a href="https://dotnet.microsoft.com/en-us/download/dotnet-framework/net35-sp1" rel="noopener noreferrer"&gt;.NET Framework 3.5&lt;/a&gt; from the Windows Features.&lt;/p&gt;

&lt;h1&gt;
  
  
  Check you have Powershell installed
&lt;/h1&gt;

&lt;p&gt;In recent versions of Windows, PowerShell is already installed and accessible through the &lt;a href="https://support.microsoft.com/en-us/topic/6453ce98-da91-476f-8651-5c14d5777c20#:~:text=In%20Windows%2011%2022H2%2C%20the,an%20instance%20of%20Windows%20Terminal" rel="noopener noreferrer"&gt;terminal&lt;/a&gt; application. At the time of writing, the pre-installed version is &lt;code&gt;5.1.22621.963&lt;/code&gt;. You can also install the latest version from the &lt;a href="https://www.microsoft.com/en-us/p/powershell/9mz1snwt0n5d?activetab=pivot:overviewtab" rel="noopener noreferrer"&gt;Microsoft Store&lt;/a&gt; (7.3.2 at the time of writing).&lt;/p&gt;

&lt;h1&gt;
  
  
  Clone the Jenkins repository and build the MSI
&lt;/h1&gt;

&lt;h1&gt;
  
  
  Clone the Jenkins packaging repository
&lt;/h1&gt;

&lt;p&gt;Choose your &lt;code&gt;git&lt;/code&gt; tool and clone the &lt;a href="https://github.com/jenkinsci/packaging.git" rel="noopener noreferrer"&gt;Jenkins packaging repository&lt;/a&gt; on your machine.&lt;/p&gt;

&lt;h1&gt;
  
  
  Prepare the build
&lt;/h1&gt;

&lt;p&gt;Open a &lt;code&gt;terminal&lt;/code&gt; and go to the folder where you cloned the repository. For me it is &lt;code&gt;C:\dev\jenkins\git\ci\packaging\&lt;/code&gt;. You now have to declare where you downloaded the Jenkins war file so that the build can find it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;War&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;USERPROFILE&lt;/span&gt;&lt;span class="s2"&gt;\jenkins.war"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you ever moved it into your repository clone folder, you can use this command instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;War&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C:\dev\jenkins\git\ci\packaging\msi\build\jenkins.war"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Build the MSI
&lt;/h1&gt;

&lt;p&gt;Enter the subfolder &lt;code&gt;msi\build&lt;/code&gt; and run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;\build.ps1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For me, this was the output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;Extracting&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;components&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;JenkinsVersion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.392&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Restoring&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;packages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;before&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;All&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;packages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;listed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;packages.config&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;already&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;installed.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Building&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;MSI&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;MSBuild&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;17.4.0&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="nx"&gt;18d5aef85&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Framework&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;started&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;01/12/2022&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;20:53:30.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Project&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C:\dev\jenkins\git\ci\packaging\msi\build\jenkins.wixproj"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;on&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;targets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;SetConstants:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nx"&gt;EncodedVersion&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;2.255&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;3920&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Compile:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nx"&gt;Skipping&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Compile"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;because&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;all&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;are&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;up-to-date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;with&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;respect&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;files.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;AssignCultures:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nx"&gt;Culture:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;en-US&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Link:&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nx"&gt;C:\dev\jenkins\git\ci\packaging\msi\build\packages\WiX.3.11.1\build\..\tools\Light.exe&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-out&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\dev\jenkins\git\ci\packaging\msi\build\bi&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;n\Release\en-US\jenkins-2.392.msi&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-pdbout&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\dev\jenkins\git\ci\packaging\msi\build\bin\Release\en-US\jenkins-2.392.wixpdb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-sw1076&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-cultures&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;en-US&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\S&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;upport\users\jenkins\git\ci\packaging\packaging\msi\build\packages\WiX.3.11.1\build\..\tools\\WixUIExtension.dll&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\dev\jenkins\git\ci\packaging\msi\bu&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;ild\packages\WiX.3.11.1\build\..\tools\\WixNetFxExtension.dll&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\dev\jenkins\git\ci\packaging\msi\build\packages\WiX.3.11.1\build\..\tools\\WixUtilExte&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;nsion.dll&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;\msiext-1.5\WixExtensions\WixCommonUIExtension.dll&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-ext&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\dev\jenkins\git\ci\packaging\msi\build\packages\WiX.3.11.1\build\..\tools\\WixFir&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;ewallExtension.dll&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-fv&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-loc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;jenkins_en-US.wxl&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-spdb&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-contentsfile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;obj\Release\jenkins.wixproj.BindContentsFileListen-US.txt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-outputsfile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;obj\Release\jenkins.wixproj.BindOutputs&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;FileListen-US.txt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-builtoutputsfile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;obj\Release\jenkins.wixproj.BindBuiltOutputsFileListen-US.txt&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;-wixprojectfile&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\dev\jenkins\git\ci\packaging\msi\build\&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;jenkins.wixproj&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;obj\Release\jenkins.wixobj&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;Windows&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Installer&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;XML&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Toolset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Linker&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;3.11.1.2318&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="n"&gt;Copyright&lt;/span&gt;&lt;span class="w"&gt; &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="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;NET&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Foundation&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;contributors.&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;All&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;rights&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;reserved.&lt;/span&gt;&lt;span class="w"&gt;

  &lt;/span&gt;&lt;span class="n"&gt;jenkins&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\dev\jenkins\git\ci\packaging\msi\build\bin\Release\en-US\jenkins-2.392.msi&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;Done&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Building&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Project&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"C:\dev\jenkins\git\ci\packaging\msi\build\jenkins.wixproj"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;default&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;targets&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;span class="n"&gt;Build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;succeeded.&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Warning&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Elapsed&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;00:00:08.26&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Find the MSI file
&lt;/h1&gt;

&lt;p&gt;The MSI file is located in the &lt;code&gt;.\bin\Release\en-US\&lt;/code&gt; folder. You will find there the generated MSI file and its &lt;code&gt;sha256&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ls&lt;/span&gt;&lt;span class="w"&gt;

    &lt;/span&gt;&lt;span class="n"&gt;Directory:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;C:\dev\jenkins\git\ci\packaging\msi\build\bin\Release\en-US&lt;/span&gt;&lt;span class="w"&gt;


&lt;/span&gt;&lt;span class="n"&gt;Mode&lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="nx"&gt;LastWriteTime&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="nx"&gt;Length&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;Name&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="o"&gt;----&lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;&lt;span class="o"&gt;-------------&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="o"&gt;------&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;----&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nt"&gt;-a&lt;/span&gt;&lt;span class="o"&gt;----&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="n"&gt;/12/2022&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nx"&gt;20:53&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nx"&gt;105107456&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;jenkins-2.392.msi&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nt"&gt;-a&lt;/span&gt;&lt;span class="o"&gt;----&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="mi"&gt;01&lt;/span&gt;&lt;span class="n"&gt;/12/2022&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nx"&gt;20:53&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;&lt;span class="nx"&gt;84&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;jenkins-2.392.msi.sha256&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>ci</category>
      <category>jenkins</category>
      <category>windows</category>
    </item>
  </channel>
</rss>
