<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Harianto van Insulinde]]></title><description><![CDATA[Code findings. Make-my-life-easy deployments. Guide for myself. Pioneering ideas; just because everyone does the same, doesn’t mean they’re right!]]></description><link>https://blog.sylo.space/</link><image><url>https://blog.sylo.space/favicon.png</url><title>Harianto van Insulinde</title><link>https://blog.sylo.space/</link></image><generator>Ghost 5.25</generator><lastBuildDate>Wed, 22 Jul 2026 21:07:36 GMT</lastBuildDate><atom:link href="https://blog.sylo.space/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Making ACME-DNS self-host work]]></title><description><![CDATA[So you‘ve got a VPS and want to self-host ACME-DNS for your new domain CA certificates]]></description><link>https://blog.sylo.space/making-acme-dns-self-host-work/</link><guid isPermaLink="false">67138a6ffdb218000123aee0</guid><category><![CDATA[Alpine]]></category><category><![CDATA[docker]]></category><dc:creator><![CDATA[Harianto van Insulinde]]></dc:creator><pubDate>Sat, 19 Oct 2024 13:34:30 GMT</pubDate><content:encoded><![CDATA[<p>Your new domain: <code>newdomain.com</code><br>Your ACME DNS self-host domain: <code>myacmedns.com</code></p><blockquote>Example domain names.</blockquote><h1 id="client">Client</h1><p>So you&#x2018;ve got a domain <code>newdomain.com</code> and you want to have certificates with wildcard such as <code>*.newdomain.com</code>.</p><p>Let&#x2019;s build our own client:</p><pre><code># directory structure
/docker/acmedns-client/
  build/
    Dockerfile
  docker-build.sh
  docker-run.sh
  acmedns-client.sh
  certbot.sh</code></pre><figure class="kg-card kg-code-card"><pre><code class="language-Dockerfile">FROM alpine:latest as BUILDER

RUN apk add --no-cache go git

WORKDIR /data

RUN git clone --depth 1 https://github.com/acme-dns/acme-dns-client &amp;&amp; \
    cd acme-dns-client &amp;&amp; \
    go get &amp;&amp; \
    go build

FROM alpine:latest
RUN apk add --no-cache certbot

# Copy the built binary from the BUILDER stage
COPY --from=BUILDER /data/acme-dns-client/acme-dns-client /usr/local/bin/acme-dns-client

CMD [&quot;/usr/local/bin/acme-dns-client&quot;]</code></pre><figcaption>alpine:<strong>3.20.3</strong>, file: <code>build/Dockerfile</code></figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-sh">#!/bin/bash
docker build -t harianto/acmedns-client -f build/Dockerfile build</code></pre><figcaption>file: docker-build.sh, docker image: <code>harianto/acmedns-client</code></figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-sh">#!/bin/bash
mkdir -p data/letsencrypt
docker run --rm -v ${pwd}/data/letsencrypt:/etc/letsencrypt -it harianto/acmedns-client sh</code></pre><figcaption>File: docker-run.sh</figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-sh">#!/bin/bash
mkdir -p data/letsencrypt
docker run --rm \
-v ${pwd}/data/letsencrypt:/etc/letsencrypt \
-it harianto/acmedns-client certbot \
  certonly \
  --manual \
  --preferred-challenges dns \
  --server https://acme-staging-v02.api.letsencrypt.org/directory \
  --agree-tos \
  -m letsencrypt@newdomain.com \
  --no-eff-email \
  --manual-auth-hook &apos;/usr/local/bin/acme-dns-client&apos; \
  &quot;$@&quot;</code></pre><figcaption>File: certbot.sh</figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-sh">#!/bin/bash
docker run --rm \
-v ${pwd}/data/letsencrypt:/etc/letsencrypt \
-it harianto/acmedns-client acme-dns-client &quot;$@&quot;
</code></pre><figcaption>File: acmedns-client.sh</figcaption></figure><blockquote>Don&#x2019;t for get to <code>chmod +x</code> your <code>*.sh</code> files</blockquote><h2 id="following-scripts-to-run">Following scripts to run</h2><ul><li>Build once: <code>./docker-build.sh</code>. Your docker image would be <code>harianto/acmedns-client</code></li></ul><blockquote>From this point you can run Docker Image <code>harianto/acmedns-client</code> however you want.</blockquote><ul><li>Register once: <code>./acmedns-client.sh register -s <a href="https://auth.acme-dns.io">https://auth.acme-dns.io</a> --dangerous -d newdomain.com</code></li></ul><blockquote>We&#x2019;ll be using <code>https://auth.acme-dns.io</code> for test, before we create our server, for example: <code>https://auth.myacmedns.com</code></blockquote><blockquote>You can ignore CAA record, for now.</blockquote><p>It will prompt like this:</p><pre><code class="language-shell">[*] New acme-dns account for domain newdomain.com successfully registered!

Do you want acme-dns-client to monitor the CNAME record change? [Y/n]: n
Domain:         7f1449b3-9371-4b6a-a472-6ab79764dae7.auth.acme-dns.io

To finalize the setup, you need to create a CNAME record pointing from _acme-challenge.newdomain.com
to the newly created acme-dns domain 7f1449b3-9371-4b6a-a472-6ab79764dae7.auth.acme-dns.io

A correctly set up CNAME record should look like the following:

_acme-challenge.newdomain.com.     IN      CNAME   7f1449b3-9371-4b6a-a472-6ab79764dae7.auth.acme-dns.io.</code></pre><p>You have to go to your DNS Settings in the Control Panel for your <code>newdomain.com</code>. And add a record:</p><pre><code>_acme-challenge.newdomain.com.     IN      CNAME   7f1449b3-9371-4b6a-a472-6ab79764dae7.auth.acme-dns.io.</code></pre><p>That might look like this.</p><figure class="kg-card kg-image-card"><img src="https://blog.sylo.space/content/images/2024/10/Screenshot-2024-10-19-at-15.47.15.png" class="kg-image" alt loading="lazy" width="1130" height="288" srcset="https://blog.sylo.space/content/images/size/w600/2024/10/Screenshot-2024-10-19-at-15.47.15.png 600w, https://blog.sylo.space/content/images/size/w1000/2024/10/Screenshot-2024-10-19-at-15.47.15.png 1000w, https://blog.sylo.space/content/images/2024/10/Screenshot-2024-10-19-at-15.47.15.png 1130w" sizes="(min-width: 720px) 720px"></figure><ul><li>Then run certbot (every 3 months) and see magic happens: </li></ul><figure class="kg-card kg-code-card"><pre><code class="language-bash">./certbot.sh -d newdomain.com -d *.newdomain.com</code></pre><figcaption>Your certificates will be stored in <code>./data/letsencrypt/live/newdomain.com</code></figcaption></figure><blockquote>Your certificates will be saved in <code>./data/letsencrypt/live/newdomain.com</code> directory.</blockquote><p>From this point, your should not having errors. Then you can create your own self-host ACME DNS. And have to register again with new (but once) and change your CNAME record (once again).</p><h1 id="server">Server</h1><p>So You want to self-host your ACME-DNS on <code>myacmedns.com</code>.</p><pre><code># directory structure
/docker/acmedns</code></pre><h2 id="build">Build</h2><p>We can use the Docker Image <code>joohoi/acme-dns</code> or we build from his git repository.</p><blockquote>There were few bumps installing, but one of the solution is to change Dockerfile and add new environment variable before <code>go build</code>.</blockquote><p>Let&#x2019;s create a directory: <code>/docker/acmedns</code></p><figure class="kg-card kg-code-card"><pre><code class="language-bash"># create directory
mkdir -p /docker/acmedns
# goto
cd /docker/acmedns
# git clone: depth 1
git clone --depth 1 https://github.com/acme-dns/acme-dns.git build
# create empty file and chmod +x
touch docker-build.sh; chmod +x docker-build.sh</code></pre><figcaption>Shell</figcaption></figure><blockquote>At this point <code>build</code> folder is created, and then we create <code>docker-build.sh</code> file.</blockquote><figure class="kg-card kg-code-card"><pre><code class="language-sh">#!/bin/bash
docker build -t harianto/acmedns-server -f build/Dockerfile build</code></pre><figcaption>File: /docker/acmedns/docker-build.sh, docker image: harianto/acmedns-server</figcaption></figure><blockquote>running <code>./docker-build.sh</code> now, you find some errors.</blockquote><p>Edit: <code>/docker/acmedns/build/Dockerfile</code></p><p>Edit a line and inlude this <code>CGO_CFLAGS=&quot;-D_LARGEFILE64_SOURCE&quot;</code> </p><pre><code># code here ...
RUN CGO_ENABLED=1 CGO_CFLAGS=&quot;-D_LARGEFILE64_SOURCE&quot; go build

# code here ...</code></pre><p>Dockerfile should look similar like this:</p><figure class="kg-card kg-code-card"><pre><code class="language-Dockerfile">FROM golang:alpine AS builder
LABEL maintainer=&quot;joona@kuori.org&quot;

RUN apk add --update gcc musl-dev git

ENV GOPATH /tmp/buildcache
RUN git clone https://github.com/joohoi/acme-dns /tmp/acme-dns
WORKDIR /tmp/acme-dns
RUN CGO_ENABLED=1 CGO_CFLAGS=&quot;-D_LARGEFILE64_SOURCE&quot; go build

FROM alpine:latest

WORKDIR /root/
COPY --from=builder /tmp/acme-dns .
RUN mkdir -p /etc/acme-dns
RUN mkdir -p /var/lib/acme-dns
RUN rm -rf ./config.cfg
RUN apk --no-cache add ca-certificates &amp;&amp; update-ca-certificates

VOLUME [&quot;/etc/acme-dns&quot;, &quot;/var/lib/acme-dns&quot;]
ENTRYPOINT [&quot;./acme-dns&quot;]
EXPOSE 53 80 443
EXPOSE 53/udp</code></pre><figcaption>line changed: <code>RUN CGO_ENABLED=1 CGO_CFLAGS=&quot;-D_LARGEFILE64_SOURCE&quot; go build</code></figcaption></figure><p>Now we can build.</p><figure class="kg-card kg-code-card"><pre><code class="language-bash"># goto dir
cd /docker/acmedns
# build it
./docker-build.sh</code></pre><figcaption>Docker Image <code>harianto/acmedns-server</code> is created</figcaption></figure><h2 id="create-docker-composeyml">Create docker-compose.yml</h2><figure class="kg-card kg-code-card"><pre><code class="language-yml"># create volume: docker volume create --name=letsencrypt
#volumes:
#  letsencrypt:
#    external: true

services:
  acmedns:
    image: harianto/acmedns-server
    container_name: acmedns
    #network_mode: host
    ports:
      - &quot;53:53&quot;
      - &quot;53:53/udp&quot;
      - &quot;80:80&quot;
      - &quot;443:443&quot;
    volumes:
      - ./data/acmedns_config:/etc/acme-dns:ro
      - ./data/acmedns_acmd-dns:/var/lib/acme-dns
      #- letsencrypt:/etc/letsencrypt:ro
    restart: unless-stopped  # Optional: to ensure the container restarts on failure</code></pre><figcaption>File: /docker/acmedns/docker-compose.yml</figcaption></figure><blockquote>Parts that are commented <code>#</code> would be handy for advanced things, uncomment them any time.</blockquote><blockquote>Running with: <code>docker-compose up</code> you might see an error that no <code>config.cfg</code> is available.</blockquote><p>You can copy <code>config.cfg</code> from the <code>build</code> folder and paste it to <code>./data/acmedns_config</code></p><figure class="kg-card kg-code-card"><pre><code class="language-ini">[general]
# DNS interface. Note that systemd-resolved may reserve port 53 on 127.0.0.53
# In this case acme-dns will error out and you will need to define the listening interface
# for example: listen = &quot;127.0.0.1:53&quot;
listen = &quot;0.0.0.0:53&quot;
# protocol, &quot;both&quot;, &quot;both4&quot;, &quot;both6&quot;, &quot;udp&quot;, &quot;udp4&quot;, &quot;udp6&quot; or &quot;tcp&quot;, &quot;tcp4&quot;, &quot;tcp6&quot;
protocol = &quot;both&quot;
# domain name to serve the requests off of
domain = &quot;auth.myacmedns.com&quot;
# zone name server
nsname = &quot;auth.myacmedns.com&quot;
# admin email address, where @ is substituted with .
nsadmin = &quot;admin.myacmedns.com&quot;
# predefined records served in addition to the TXT
records = [
    # domain pointing to the public IP of your acme-dns server 
    &quot;auth.myacmedns.com. A 31.14.98.159&quot;,
    # specify that auth.myacmedns.com will resolve any *.auth.myacmedns.com records
    &quot;auth.myacmedns.com. NS auth.myacmedns.com.&quot;,
]
# debug messages from CORS etc
debug = false

[database]
# Database engine to use, sqlite3 or postgres
engine = &quot;sqlite3&quot;
# Connection string, filename for sqlite3 and postgres://$username:$password@$host/$db_name for postgres
# Please note that the default Docker image uses path /var/lib/acme-dns/acme-dns.db for sqlite3
connection = &quot;/var/lib/acme-dns/acme-dns.db&quot;
# connection = &quot;postgres://user:password@localhost/acmedns_db&quot;

[api]
# listen ip eg. 127.0.0.1
ip = &quot;0.0.0.0&quot;
# disable registration endpoint
disable_registration = false
# listen port, eg. 443 for default HTTPS
port = &quot;443&quot;
# possible values: &quot;letsencrypt&quot;, &quot;letsencryptstaging&quot;, &quot;cert&quot;, &quot;none&quot;
tls = &quot;cert&quot;
# only used if tls = &quot;cert&quot;
tls_cert_privkey = &quot;/etc/letsencrypt/live/myacmedns.com/privkey.pem&quot;
tls_cert_fullchain = &quot;/etc/letsencrypt/live/myacmedns.com/fullchain.pem&quot;
# only used if tls = &quot;letsencrypt&quot;
acme_cache_dir = &quot;api-certs&quot;
# optional e-mail address to which Let&apos;s Encrypt will send expiration notices for the API&apos;s cert
notification_email = &quot;&quot;
# CORS AllowOrigins, wildcards can be used
corsorigins = [
    &quot;*&quot;
]
# use HTTP header to get the client ip
use_header = false
# header name to pull the ip address / list of ip addresses from
header_name = &quot;X-Forwarded-For&quot;

[logconfig]
# logging level: &quot;error&quot;, &quot;warning&quot;, &quot;info&quot; or &quot;debug&quot;
loglevel = &quot;debug&quot;
# possible values: stdout, TODO file &amp; integrations
logtype = &quot;stdout&quot;
# file path for logfile TODO
# logfile = &quot;./acme-dns.log&quot;
# format, either &quot;json&quot; or &quot;text&quot;
logformat = &quot;text&quot;
</code></pre><figcaption>File: /docker/acmedns/data/acmedns_config/config.cfg</figcaption></figure><blockquote>Change to <code>port=&quot;80</code>&quot;, and <code>tls=&quot;none</code>&quot; if your server don&#x2019;t have certificates yet. And register server running this:<br><code>./acmedns-client.sh register -s <a href="https://auth.acme-dns.io/">http://auth.myacmedns.com</a></code>.</blockquote><h2 id="change-dns-settings">Change DNS Settings</h2><p>For my newly discovery that works. Your DNS Settings need to able to add <code>NS</code> records. I&#x2019;ve tried with <code>A</code> records, but it doesn&#x2019;t work.</p><blockquote>There can only be one record with <code>auth</code>.</blockquote><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2024/10/Screenshot-2024-10-19-at-12.39.57.png" class="kg-image" alt loading="lazy" width="2000" height="501" srcset="https://blog.sylo.space/content/images/size/w600/2024/10/Screenshot-2024-10-19-at-12.39.57.png 600w, https://blog.sylo.space/content/images/size/w1000/2024/10/Screenshot-2024-10-19-at-12.39.57.png 1000w, https://blog.sylo.space/content/images/size/w1600/2024/10/Screenshot-2024-10-19-at-12.39.57.png 1600w, https://blog.sylo.space/content/images/2024/10/Screenshot-2024-10-19-at-12.39.57.png 2362w" sizes="(min-width: 720px) 720px"><figcaption>DNS record from a self-host where your own ACME DNS would be in.</figcaption></figure><blockquote><code>@</code> points to your public address <code>A</code> <code>11.22.33.44</code> ,<br><code>auth</code>.myacmedns.com points to <code>NS</code> <code>myacmedns.com.</code></blockquote><h2 id="let%E2%80%99s-go-back-to-our-client">Let&#x2019;s go back to our Client</h2><p>Going back to your <code>newdomain.com</code> terminal, we need to run some commands again, to <em>register</em> and creating <em>certificates</em>.</p><h3 id="register-once">Register once</h3><p>With your self-host server, you can update new server path.</p><figure class="kg-card kg-code-card"><pre><code class="language-bash"># goto
cd /docker/acmedns-client
# register once
./acmedns-client.sh register -s https://auth.myacmedns.com
</code></pre><figcaption>Is your Acme DNS config.cfg port to 80, then use: http://auth.myacmedns.com</figcaption></figure><p>Follow instructions and remember <code>CNAME</code> record, for example:</p><pre><code>_acme-challenge.newdomain.com.     IN      CNAME   7f1449b3-9371-4b6a-a472-6ab79764dae7.auth.myacmedns.com.</code></pre><h3 id="update-dns-settings-once">Update DNS Settings once</h3><p>Go to your control panel and update your DNS records.</p><h3 id="get-your-certificates-and-see-the-magic-happens">Get your certificates and see the magic happens</h3><pre><code class="language-bash"># goto
cd /docker/acmedns-client
# get certificates
./certbot.sh -d newdomain.com -d *.newdomain.com</code></pre><h1 id="does-it-work-for-you">Does it work for you?</h1><p>Let me know if this works for you, or give me a thumbs up!</p>]]></content:encoded></item><item><title><![CDATA[Never buy items without PayPal or Klarna]]></title><description><![CDATA[<p>When websites made so good, thats good to be true. But it is not.</p><p>Bought item from Amsterdam, but they deliver from China &#xA0;</p>]]></description><link>https://blog.sylo.space/never-buy-items-without-paypal-or-klarna/</link><guid isPermaLink="false">670d941bfdb218000123aec6</guid><category><![CDATA[Journal]]></category><category><![CDATA[SCAM]]></category><dc:creator><![CDATA[Harianto van Insulinde]]></dc:creator><pubDate>Mon, 14 Oct 2024 22:02:12 GMT</pubDate><content:encoded><![CDATA[<p>When websites made so good, thats good to be true. But it is not.</p><p>Bought item from Amsterdam, but they deliver from China &#xA0;</p>]]></content:encoded></item><item><title><![CDATA[My Sieve script]]></title><description><![CDATA[<p>Anti spam script</p><p></p><pre><code>require [&quot;fileinto&quot;, &quot;body&quot;, &quot;imap4flags&quot;, &quot;regex&quot;, &quot;envelope&quot;, &quot;spamtest&quot;, &quot;relational&quot;, &quot;comparator-i;ascii-numeric&quot;, &quot;fileinto&quot;, &quot;imap4flags&quot;];

# Check for spam score
if allof (spamtest :value &quot;ge&quot; :comparator &quot;i;ascii-numeric&</code></pre>]]></description><link>https://blog.sylo.space/my-sieve-script/</link><guid isPermaLink="false">670c3aeffdb218000123aeb3</guid><category><![CDATA[Journal]]></category><category><![CDATA[Email]]></category><category><![CDATA[Sieve]]></category><dc:creator><![CDATA[Harianto van Insulinde]]></dc:creator><pubDate>Sun, 13 Oct 2024 21:27:40 GMT</pubDate><content:encoded><![CDATA[<p>Anti spam script</p><p></p><pre><code>require [&quot;fileinto&quot;, &quot;body&quot;, &quot;imap4flags&quot;, &quot;regex&quot;, &quot;envelope&quot;, &quot;spamtest&quot;, &quot;relational&quot;, &quot;comparator-i;ascii-numeric&quot;, &quot;fileinto&quot;, &quot;imap4flags&quot;];

# Check for spam score
if allof (spamtest :value &quot;ge&quot; :comparator &quot;i;ascii-numeric&quot; &quot;5&quot;) {
    fileinto &quot;Spam&quot;;
    stop;
}

# Check for suspicious subject lines
if header :regex &quot;Subject&quot; [&quot;(?i)\\b(viagra|cialis|enlargement|\\$\\$\\$)\\b&quot;] {
    fileinto &quot;Spam&quot;;
    stop;
}

# Check for missing or suspicious From headers
if anyof (not exists &quot;From&quot;,
          header :regex &quot;From&quot; &quot;(?i)\\b(mailer-daemon|postmaster)@&quot;) {
    fileinto &quot;Spam&quot;;
    stop;
}

# Check for suspicious content in the body
if body :raw :contains [&quot;click here&quot;, &quot;limited time offer&quot;, &quot;act now&quot;, &quot;congratulations&quot;, &quot;you&apos;ve won&quot;] {
    fileinto &quot;Spam&quot;;
    stop;
}

# Check for Authentication-Results header
if header :contains &quot;Authentication-Results&quot; [&quot;dkim=fail&quot;, &quot;spf=fail&quot;, &quot;dmarc=fail&quot;] {
    fileinto &quot;Spam&quot;;
    stop;
}

# If none of the above conditions are met, keep the message in the inbox
keep;
</code></pre>]]></content:encoded></item><item><title><![CDATA[Poste.io Mailserver]]></title><description><![CDATA[<p>My poste.io configuration together with my xnmp-vhosts docker and letsencrypt certificates</p><p></p><h1 id="xnmp-vhost">XNMP-VHOST</h1><p>And part configuration from my other Docker Composer.</p><figure class="kg-card kg-code-card"><pre><code class="language-yml"># create volume: docker volume create --name=letsencrypt
volumes:
  letsencrypt:
    external: true

services:
  nginx:
    image: nginx:alpine
    restart: always
    links:
      - 8-0-0-fpm-ext
    ports:
      - &quot;80:80&quot;
      - &quot;</code></pre></figure>]]></description><link>https://blog.sylo.space/poste-io-mailserver/</link><guid isPermaLink="false">6702e80bfdb218000123ae70</guid><category><![CDATA[Draft]]></category><category><![CDATA[docker]]></category><dc:creator><![CDATA[Harianto van Insulinde]]></dc:creator><pubDate>Sun, 06 Oct 2024 19:49:52 GMT</pubDate><content:encoded><![CDATA[<p>My poste.io configuration together with my xnmp-vhosts docker and letsencrypt certificates</p><p></p><h1 id="xnmp-vhost">XNMP-VHOST</h1><p>And part configuration from my other Docker Composer.</p><figure class="kg-card kg-code-card"><pre><code class="language-yml"># create volume: docker volume create --name=letsencrypt
volumes:
  letsencrypt:
    external: true

services:
  nginx:
    image: nginx:alpine
    restart: always
    links:
      - 8-0-0-fpm-ext
    ports:
      - &quot;80:80&quot;
      - &quot;443:443&quot;
    volumes:
      - ./data/nginx/enabled:/etc/nginx/conf.d
      - ./data/nginx/snippets:/nginx/snippets
      - ./data/nginx/certificates:/nginx/certificates
      - letsencrypt:/etc/letsencrypt:ro
    volumes_from:
      - data

  8-0-0-fpm-ext:
    build: build/8-0-0-fpm-ext
    restart: always
    volumes_from:
      - data

  db:
    image: mariadb:10.5.8 #v10.5.8
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root
    volumes:
      - ./data/db:/var/lib/mysql

  dbadmin:
    image: phpmyadmin/phpmyadmin
    restart: always
    environment:
      PMA_HOST: db
      PMA_USER: root # Remove line for production
      PMA_PASSWORD: root # Remove line for production
    depends_on:
      - db

  data:
    image: alpine:latest
    command: echo &quot;--- Docker data volume READY.&quot;
    volumes:
      - ./data/vhosts:/vhosts
      - ./data/tmp:/tmp

# create network: docker network create xnmp-network
networks:
  default:
    name: xnmp-network
    external: true</code></pre><figcaption>harianto XNMP-VHOST: docker-compose.yml</figcaption></figure><blockquote>Follow my guide here: <a href="https://blog.sylo.space/guide-to-install-nginx-php-mariadb-phpmyadmin-in-docker/">XNMP-VHOSTS</a><br>My custom Docker build with Letsencrypt inside</blockquote><p></p><h1 id="my-posteio">My Poste.io</h1><figure class="kg-card kg-code-card"><pre><code class="language-yml"># create volume: docker volume create --name=letsencrypt
volumes:
  letsencrypt:
    external: true

services:
  mailserver:
    image: analogic/poste.io:latest
    hostname: mail.harianto.dev
    environment:
      - TZ=Europe/Amsterdam
      - MODE=pro
    volumes:
      - ./data/mailserver:/data
      - letsencrypt:/etc/letsencrypt
    ports:
      # - 80:80/tcp   # HTTP
      # - 443:443/tcp # HTTPS
      - 25:25/tcp   # SMTP
      - 465:465/tcp # SMTPS
      - 587:587/tcp # SMTP
      - 110:110/tcp # POP3
      - 995:995/tcp # POP3S
      - 143:143/tcp # IMAP
      - 993:993/tcp # IMAPS
      - 4190:4190/tcp # Sieve
    # network_mode: host

# create network: docker network create xnmp-network
networks:
  default:
    name: xnmp-network
    external: true</code></pre><figcaption>Poste.io Mailserver: docker-compose.yml</figcaption></figure><pre><code class="language-bash">docker-compose exec mailserver bash</code></pre><p>inside mailserver terminal</p><figure class="kg-card kg-code-card"><pre><code class="language-bash"># add user `mail` to `root` group
sudo adduser mail root

# make a symlink from a mounted letsencrypt
cd /data/ssl
rm -rf letsencrypt
ln -nsf /etc/letsencrypt/live /data/ssl/letsencrypt

# exit docker terminal
exit</code></pre><figcaption>Mailserver Terminal</figcaption></figure><blockquote>This make sure <code>mail</code> user have read/write access to <code>root</code> files, so it can read letsencrypt files</blockquote><p>Restart Docker</p><pre><code class="language-bash">docker-compose restart mailserver</code></pre>]]></content:encoded></item><item><title><![CDATA[Created my own Nuxt 3 template]]></title><description><![CDATA[<p>I&#x2019;ve just created my own starter Nuxt 3 boilerplate. It save me some time for creating new project and tools.</p><p>Just run:</p><pre><code class="language-bash">npx nuxi init my-nuxt-app -t gh:HariantoAtWork/nuxt-3-boilerplate</code></pre>]]></description><link>https://blog.sylo.space/created-my-own-nuxt-3-template/</link><guid isPermaLink="false">66ceff1daeb53d0001dc1b45</guid><dc:creator><![CDATA[Harianto van Insulinde]]></dc:creator><pubDate>Wed, 28 Aug 2024 10:44:20 GMT</pubDate><content:encoded><![CDATA[<p>I&#x2019;ve just created my own starter Nuxt 3 boilerplate. It save me some time for creating new project and tools.</p><p>Just run:</p><pre><code class="language-bash">npx nuxi init my-nuxt-app -t gh:HariantoAtWork/nuxt-3-boilerplate</code></pre>]]></content:encoded></item><item><title><![CDATA[@harianto/axioscancelable]]></title><description><![CDATA[<p>I recently developed a new <a href="https://www.npmjs.com/package/@harianto/axioscancelable">NPM</a> module, an enhanced version of axiosBluebird with ESM support. This module simplifies asynchronous operations and provides better control over requests. Create instances with AbortController included and experience the benefits. Download version v1.4.0 today!</p><h1 id="install">Install</h1><figure class="kg-card kg-code-card"><pre><code class="language-bash">npm i @harianto/axioscancelable</code></pre><figcaption>Terminal</figcaption></figure><!--kg-card-begin: markdown--><h1 id="axioscancelable">AxiosCancelable</h1>
<p>Axios with</p>]]></description><link>https://blog.sylo.space/harianto-axioscancelable/</link><guid isPermaLink="false">66cc60daaeb53d0001dc1afd</guid><category><![CDATA[Journal]]></category><category><![CDATA[javascript]]></category><dc:creator><![CDATA[Harianto van Insulinde]]></dc:creator><pubDate>Mon, 26 Aug 2024 11:12:09 GMT</pubDate><content:encoded><![CDATA[<p>I recently developed a new <a href="https://www.npmjs.com/package/@harianto/axioscancelable">NPM</a> module, an enhanced version of axiosBluebird with ESM support. This module simplifies asynchronous operations and provides better control over requests. Create instances with AbortController included and experience the benefits. Download version v1.4.0 today!</p><h1 id="install">Install</h1><figure class="kg-card kg-code-card"><pre><code class="language-bash">npm i @harianto/axioscancelable</code></pre><figcaption>Terminal</figcaption></figure><!--kg-card-begin: markdown--><h1 id="axioscancelable">AxiosCancelable</h1>
<p>Axios with custom CancelablePromise cancelation</p>
<blockquote>
<p><em>Node</em>: v20.16.0<br>
<em>NPM</em>: 10.8.1</p>
</blockquote>
<blockquote>
<p>This is ESM variant from NPM: axiosbluebird.</p>
</blockquote>
<h2 id="how-to-usefactoryaxioscancelable">How to use - factoryAxiosCancelable</h2>
<pre><code class="language-js">import { factoryAxiosCancelable, isCancel } from &apos;@harianto/axioscancelable&apos;
</code></pre>
<h3 id="method-get">method: GET</h3>
<pre><code class="language-js">const getDefaultConfig = {
  method: &apos;get&apos;,
  url: &apos;https://api.sylo.space/test/axioscancelable/data&apos;
}

// Factory (Instantiate)
const getData = factoryAxiosCancelable(getDefaultConfig)

// See Axios Config: params
const firstRequest = getData({
  params: {
    id: 12345
  }
})

firstRequest
  .then(data =&gt; {
    console.log(&apos;SUCCESS firstRequest!!!&apos;, data)
  })
  .catch(error =&gt; {
    if (isCancel(error)) {
      console.log(&apos;Request aborted firstRequest&apos;)
    } else {
      console.error(error)
    }
  })

const secondRequest = getData({
  params: {
    id: 67890
  }
})

secondRequest
  .then(data =&gt; {
    console.log(&apos;SUCCESS secondRequest!!!&apos;, data)
  })
  .catch(error =&gt; {
    if (isCancel(error)) {
      console.log(&apos;Request aborted secondRequest&apos;)
    } else {
      console.error(error)
    }
  })

// Note: The `firstRequest` gets aborted
</code></pre>
<h3 id="method-post">Method: POST</h3>
<pre><code class="language-js">const postDefaultConfig = {
  method: &apos;post&apos;,
  url: &apos;https://api.sylo.space/test/axioscancelable/data&apos;
}

// Factoried (Instantiate)
const postData = factoryAxiosCancelable(postDefaultConfig)

// See Axios Config: data
const thirdRequest = postData({
  data: {
    id: 12345
  }
})

thirdRequest
  .then(data =&gt; {
    console.log(&apos;SUCCESS thirdRequest!!!&apos;, data)
  })
  .catch(error =&gt; {
    if (isCancel(error)) {
      console.log(&apos;Request aborted thirdRequest&apos;)
    } else {
      console.error(error)
    }
  })

const fourthRequest = postData({
  data: {
    id: 67890
  }
})

fourthRequest
  .then(data =&gt; {
    console.log(&apos;SUCCESS fourthRequest!!!&apos;, data)
  })
  .catch(error =&gt; {
    if (isCancel(error)) {
      console.log(&apos;Request aborted fourthRequest&apos;)
    } else {
      console.error(error)
    }
  })

// Note: The `thirdRequest` gets aborted
</code></pre>
<h2 id="how-to-useaxioscancelable">How to use - axiosCancelable</h2>
<pre><code class="language-js">import axiosCancelable, { isCancel } from &apos;@harianto/axioscancelable&apos;
</code></pre>
<h3 id="axioscancelablegetor-get-delete-head-options">axiosCancelable.get - or get | delete | head | options</h3>
<pre><code class="language-js">// Factoried (Instantiate) with .get()
// axiosCancelable.get(url, [params], [config])
const getData = axiosCancelable.get()

// firstRequest
getData(&apos;https://api.sylo.space/test/axioscancelable/data&apos;, {id: 17})
  .catch(error =&gt; {
    if (isCancel(error)) {
      console.log(&apos;Request aborted firstRequest&apos;)
    } else {
      console.error(error)
    }
  })
// secondRequest
getData(&apos;https://api.sylo.space/test/axioscancelable/data&apos;, {id: [1,2,3]})
  .then(console.log.bind(console))
// Note: `firstRequest` gets aborted
</code></pre>
<h3 id="axioscancelablepostor-post-put-patch">axiosCancelable.post - or post | put | patch</h3>
<pre><code class="language-js">import axiosCancelable, { isCancel } from &apos;@harianto/axioscancelable&apos;
</code></pre>
<pre><code class="language-js">// Factoried (Instantiate) with .post()
// axiosCancelable.post(url, data, [config])
const postData = axiosCancelable.post()

// firstRequest
postData(&apos;https://api.sylo.space/test/axioscancelable/data&apos;, {id: 17})
  .catch(error =&gt; {
    if (isCancel(error)) {
      console.log(&apos;Request aborted firstRequest&apos;)
    } else {
      console.error(error)
    }
  })
// secondRequest
postData(&apos;https://api.sylo.space/test/axioscancelable/data&apos;, {id: [1,2,3]})
  .then(console.log.bind(console))
// Note: `firstRequest` gets aborted
</code></pre>
<h2 id="how-to-useaxios-factory">How to use - axios | Factory</h2>
<blockquote>
<p>Parameters as Object in axios <a href="https://www.npmjs.com/package/axios">documentation</a></p>
</blockquote>
<blockquote>
<p>Parameters as String not supported</p>
</blockquote>
<pre><code class="language-js">import axiosCancelable, { isCancel } from &apos;@harianto/axioscancelable&apos;

// Factoried (Instantiate) with .axios()
// axiosCancelable.axios(config)
const axiosData = axiosCancelable.axios()
</code></pre>
<pre><code class="language-js">// 1st request
axiosData({
  method: &apos;post&apos;,
  url: &apos;/user/12345&apos;,
  data: {
    firstName: &apos;Fred&apos;,
    lastName: &apos;Flintstone&apos;
  }
})

// 2nd request
axiosData({
  method: &apos;get&apos;,
  url: &apos;http://bit.ly/2mTM3nY&apos;,
  responseType: &apos;stream&apos; 
})
  .then(
    response =&gt; response.data.pipe(fs.createWriteStream(&quot;ada_lovelace.jpg&quot;))
  ) // previous request (1st request) will be canceled
</code></pre>
<blockquote>
<p><code>responseType: &apos;stream&apos;</code> not yet tested</p>
</blockquote>
<h3 id="getting-data-response">Getting Data Response</h3>
<pre><code class="language-js">// Factoried (Instantiate) with .axios()
const axiosRequest = axiosCancelable.axios()
// 1st request
axiosRequest({
  method: &apos;post&apos;,
  url: &apos;/user/12345&apos;,
  data: {
    firstName: &apos;Fred&apos;,
    lastName: &apos;Flintstone&apos;
  }
})
  .then(({data}) =&gt; data)
  .catch(error =&gt; {
    if (isCancel(error)) {
      console.log(&apos;Request aborted&apos;)
    } else {
      console.error(error)
    }
  })
</code></pre>
<h2 id="examples">Examples</h2>
<h3 id="have-an-ajaxjs-filefactoryaxioscancelable">Have an ajax.js file - factoryAxiosCancelable</h3>
<pre><code class="language-js">import { factoryAxiosCancelable } from &apos;@harianto/axioscancelable&apos;
export { isCancel } from &apos;@harianto/axioscancelable&apos;

const instances = {
  getProfile: factoryAxiosCancelable({ method: &apos;get&apos;, url: &apos;/api/profile&apos; }),
  postProfile: factoryAxiosCancelable({ method: &apos;post&apos;, url: &apos;/api/profile&apos; }),
  postVerifytoken: factoryAxiosCancelable({ method: &apos;post&apos;, url: &apos;/api/verifytoken&apos; }),
  postRegister: factoryAxiosCancelable({ method: &apos;post&apos;, url: &apos;/api/register&apos; }),
  postLogin: factoryAxiosCancelable({ method: &apos;post&apos;, url: &apos;/api/login&apos; })
}
const onCanceled = error =&gt; {
  if (isCancel(error)) {
    console.log(&apos;Canceled&apos;)
  } else {
    throw error
  }
}

export const getProfile = (params = {}) =&gt;
  instances.getProfile({params}).catch(onCanceled)
export const postProfile = (data = {}) =&gt;
  instances.postProfile({data}).catch(onCanceled)
export const postVerifytoken = (data = {}) =&gt;
  instances.postVerifytoken({data}).catch(onCanceled)
export const postRegister = (data = {}) =&gt;
  instances.postRegister({data}).catch(onCanceled)
export const postLogin = (data = {}) =&gt;
  instances.postLogin({data}).catch(onCanceled)
</code></pre>
<h3 id="have-an-ajaxjs-fileaxioscancelable">Have an ajax.js file - axiosCancelable</h3>
<pre><code class="language-js">import axiosCancelable from &apos;@harianto/axioscancelable&apos;
export { isCancel } from &apos;@harianto/axioscancelable&apos;

const instances = {
  getProfile: axiosCancelable.get(&apos;/api/profile&apos;),
  postProfile: axiosCancelable.post(&apos;/api/profile&apos;),
  postVerifytoken: axiosCancelable.post(&apos;/api/verifytoken&apos;),
  postRegister: axiosCancelable.post(&apos;/api/register&apos;),
  postLogin: axiosCancelable.post(&apos;/api/login&apos;)
}
const onCanceled = error =&gt; {
  if (isCancel(error)) {
    console.log(&apos;Canceled&apos;)
  } else {
    throw error
  }
}

export const getProfile = (params = {}) =&gt;
  instances.getProfile(null, params).catch(onCanceled)
export const postProfile = (data = {}) =&gt;
  instances.postProfile(null, data).catch(onCanceled)
export const postVerifytoken = (data = {}) =&gt;
  instances.postVerifytoken(null, data).catch(onCanceled)
export const postRegister = (data = {}) =&gt;
  instances.postRegister(null, data).catch(onCanceled)
export const postLogin = (data = {}) =&gt;
  instances.postLogin(null, data).catch(onCanceled)
</code></pre>
<h2 id="methods">Methods</h2>
<p>axios ( <em>requestConfig</em>: Object ): Request with configuration</p>
<hr>
<p>delete ( <em>url</em>: String [, <em>params</em>: Object] [, <em>config</em>: Object] ): Axios request with DELETE method</p>
<p>get ( <em>url</em>: String [, <em>params</em>: Object] [, <em>config</em>: Object] ): Axios request with GET method</p>
<p>head ( <em>url</em>: String [, <em>params</em>: Object] [, <em>config</em>: Object] ): Axios request with HEAD method</p>
<p>options ( <em>url</em>: String [, <em>params</em>: Object] [, <em>config</em>: Object] ): Axios request with OPTIONS method</p>
<hr>
<p>post ( <em>url</em>: String, <em>data</em>: Object [, <em>config</em>: Object] ): Axios request with POST method</p>
<p>put ( <em>url</em>: String, <em>data</em>: Object [, <em>config</em>: Object] ): Axios request with PUT method</p>
<p>patch ( <em>url</em>: String, <em>data</em>: Object [, <em>config</em>: Object] ): Axios request with PATCH method</p>
<h2 id="note">NOTE!</h2>
<p>Param properties as array</p>
<pre><code class="language-js">params: {
  filter: [8, 16, 32]
}
</code></pre>
<p>will output:</p>
<pre><code>filter=8&amp;filter=16&amp;filter=32
</code></pre>
<p>Idealisticly:</p>
<pre><code>filter=[8,16,32]
</code></pre>
<p>But some servers can&apos;t accept brackets</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Create Vite Vue SSR with Docker]]></title><description><![CDATA[Create a project from a command-line and build Docker]]></description><link>https://blog.sylo.space/create-vite-vue-ssr-with-docker/</link><guid isPermaLink="false">65d84fe680008f00011dec4a</guid><category><![CDATA[Vue]]></category><category><![CDATA[docker]]></category><dc:creator><![CDATA[Harianto van Insulinde]]></dc:creator><pubDate>Fri, 23 Feb 2024 11:18:44 GMT</pubDate><content:encoded><![CDATA[<blockquote>Node v21.6.2 (npm v10.2.4)</blockquote><h1 id="create-project">Create project</h1><p>Let&#x2019;s use the command line to create our project <code>my-vite-vue-ssr</code>.</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">npm create vite-extra@latest my-vite-vue-ssr -- --template ssr-vue</code></pre><figcaption>Project Name: my-vite-vue-ssr</figcaption></figure><figure class="kg-card kg-code-card"><pre><code>Scaffolding project in /Users/harianto/Projects/my-vite-vue-ssr...

Done. Now run:

  cd my-vite-vue-ssr
  npm install
  npm run dev</code></pre><figcaption>Check the browser: http://localhost:5173/</figcaption></figure><p>Follow other commands then continue.</p><p>Save your current Node &amp; NPM version (for NVM).</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">node -v &gt; .nvmrc</code></pre><figcaption>Important way to know your working Node Version and use NVM to load the correct one with: <code>nvm use</code>.</figcaption></figure><p>Initiate git with <code>git init</code>. And save the Initial state.</p><pre><code class="language-bash">git init</code></pre><pre><code>Initialized empty Git repository in ~/git/my-vite-vue-ssr/.git/</code></pre><pre><code class="language-bash"># Stage all files
git add .
# Commit and Message it
git commit -avm &quot;INIT my-vite-vue-ssr Node `node -v`&quot;</code></pre><h1 id="docker-things">Docker things</h1><p>Add <code>.dockerignore</code> </p><figure class="kg-card kg-code-card"><pre><code class="language-dockerignore"># Created by .ignore support plugin (hsz.mobi)
### Node template
# Logs
/logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of &apos;npm pack&apos;
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env

# parcel-bundler cache (https://parceljs.org/)
.cache

# next.js build output
.next

# nuxt.js build output
# .nuxt

# Nuxt generate
dist

# vuepress build output
.vuepress/dist

# Serverless directories
.serverless

# IDE / Editor
.idea

# Service worker
sw.*

# Mac OSX
.DS_Store

# Vim swap files
*.swp

# VSCode Settings
.vscode

# APP IGNORES
package-lock.json
.git
.docker-mount</code></pre><figcaption>Of course you can change this for your needs</figcaption></figure><h2 id="create-docker-node-image">Create Docker Node Image</h2><p>Before we create <code>Dockerfile</code> we need to edit the <code>package.json</code>. In <code>scripts</code> we add more handy command lines.</p><p>I&#x2019;m using Docker Hub User as <code>dockeruser</code> and project name as <code>my-vite-vue-ssr</code>. Use your own preferences and change the code accordingly.</p><figure class="kg-card kg-code-card"><pre><code class="language-json">  &quot;scripts&quot;: {
    &quot;docker&quot;: &quot;cross-env NODE_ENV=production npm run dev&quot;,
    &quot;docker:build&quot;: &quot;npm run build &amp;&amp; docker build -t dockeruser/my-vite-vue-ssr .&quot;,
    &quot;docker:run&quot;: &quot;docker run --rm -p 3000:3000 dockeruser/my-vite-vue-ssr&quot;,
    &quot;docker:push&quot;: &quot;npm run docker:build &amp;&amp; docker push dockeruser/my-vite-vue-ssr&quot;
  },</code></pre><figcaption>Add these lines to your <code>package.json</code> file.</figcaption></figure><blockquote>See we&#x2019;re using port 3000 instead of 5173</blockquote><p>And move all <code>devDependencies</code> lines to <code>dependencies</code>.</p><blockquote>During Docker build the script will only check <code>dependencies</code>.</blockquote><p>See below the result.</p><figure class="kg-card kg-code-card"><pre><code class="language-json">{
  &quot;name&quot;: &quot;my-vite-vue-ssr&quot;,
  &quot;private&quot;: true,
  &quot;version&quot;: &quot;0.0.0&quot;,
  &quot;type&quot;: &quot;module&quot;,
  &quot;scripts&quot;: {
    &quot;dev&quot;: &quot;node server&quot;,
    &quot;build&quot;: &quot;npm run build:client &amp;&amp; npm run build:server&quot;,
    &quot;build:client&quot;: &quot;vite build --ssrManifest --outDir dist/client&quot;,
    &quot;build:server&quot;: &quot;vite build --ssr src/entry-server.js --outDir dist/server&quot;,
    &quot;preview&quot;: &quot;cross-env NODE_ENV=production node server&quot;,
    &quot;docker&quot;: &quot;cross-env NODE_ENV=production npm run dev&quot;,
    &quot;docker:build&quot;: &quot;npm run build &amp;&amp; docker build -t dockeruser/my-vite-vue-ssr .&quot;,
    &quot;docker:run&quot;: &quot;docker run --rm -p 3000:3000 dockeruser/my-vite-vue-ssr&quot;,
    &quot;docker:push&quot;: &quot;npm run docker:build &amp;&amp; docker push dockeruser/my-vite-vue-ssr&quot;
  },
  &quot;dependencies&quot;: {
    &quot;compression&quot;: &quot;^1.7.4&quot;,
    &quot;express&quot;: &quot;^4.18.2&quot;,
    &quot;sirv&quot;: &quot;^2.0.4&quot;,
    &quot;vue&quot;: &quot;^3.3.13&quot;,
    &quot;@vitejs/plugin-vue&quot;: &quot;^4.5.2&quot;,
    &quot;cross-env&quot;: &quot;^7.0.3&quot;,
    &quot;vite&quot;: &quot;^5.0.10&quot;
  },
  &quot;devDependencies&quot;: {}
}</code></pre><figcaption>File: <code>package.json</code>. Resulting lines</figcaption></figure><h2 id="create-dockerfile">Create Dockerfile</h2><p>Since my node version is <code>v21.6.2</code>. I&#x2019;m looking which Alpine version from <a href="https://hub.docker.com/_/node/">https://hub.docker.com/_/node/</a> and look for available version number <code>-alpine</code>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2024/02/Checking-Docker-Hub-available-Node-version.png" class="kg-image" alt loading="lazy" width="784" height="153" srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Checking-Docker-Hub-available-Node-version.png 600w, https://blog.sylo.space/content/images/2024/02/Checking-Docker-Hub-available-Node-version.png 784w" sizes="(min-width: 720px) 720px"><figcaption><code>node:21.6.2-alpine</code></figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-Dockerfile">FROM node:21.6.2-alpine

EXPOSE 3000

ENV NODE_ENV=production
ENV PORT=3000
ENV NODE_WORKDIR=/app
WORKDIR $NODE_WORKDIR

COPY package.json $NODE_WORKDIR/
RUN npm shrinkwrap
RUN npm i
COPY . $NODE_WORKDIR
RUN npm run build

CMD [&quot;npm&quot;, &quot;run&quot;, &quot;docker&quot;]</code></pre><figcaption>File: <code>Dockerfile</code>. In Docker environment the files are being copied to <code>/app</code> directory. And exposes port 3000</figcaption></figure><h2 id="build-docker-image">Build Docker Image</h2><p>Create Docker Image: <code>dockeruser/my-vite-vue-ssr</code>.</p><p>Since we made special commands in <code>package.json</code> we can build from it, without knowing long strings of commands. Just peek it in.</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">npm run docker:build</code></pre><figcaption>Command Shortcut: <code>npm run build &amp;&amp; docker build -t dockeruser/my-vite-vue-ssr .</code></figcaption></figure><p>Hopefully everything goes well, and we can look up with in command: <code>docker images</code>.</p><pre><code>REPOSITORY                         TAG                 IMAGE ID       CREATED              SIZE
dockeruser/my-vite-vue-ssr         latest              323b18f70290   About a minute ago   211MB</code></pre><h3 id="test-docker-image">Test Docker Image</h3><p>Let&#x2019;s use the command and check the web: <a href="http://localhost:3000/">http://localhost:3000/</a></p><pre><code class="language-bash">npm run docker:run</code></pre><h3 id="kill-running-image">Kill Running Image</h3><p>To kill the image, first we need to see what&#x2019;s running again on port 3000. With this command below we can see what&#x2019;s going on.</p><pre><code class="language-bash">docker ps | grep 3000</code></pre><figure class="kg-card kg-code-card"><pre><code>7d32fd2396d8   dockeruser/my-vite-vue-ssr           &quot;docker-entrypoint.s&#x2026;&quot;   13 minutes ago   Up 13 minutes   0.0.0.0:3000-&gt;3000/tcp                     stupefied_cohen</code></pre><figcaption>Remember the first column value: <code>7d32fd2396d8</code></figcaption></figure><p>Use this info <code>7d32fd2396d8</code> to kill the process.</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">docker rm -f 7d32fd2396d8</code></pre><figcaption>Killing Docker Process <code>7d32fd2396d8</code></figcaption></figure><h3 id="other-useful-docker-commands">Other useful docker commands</h3><pre><code class="language-bash"># see docker images
docker images

# remove IMAGE ID ex. baabdd69cf7a
docker rmi -f baabdd69cf7a

# see dangling images
docker images -q --filter &quot;dangling=true&quot;

# remove untagged images
docker rmi -f `docker images -q --filter &quot;dangling=true&quot;` &gt; /dev/null 2&gt;&amp;1 || echo &quot;Nothing to remove&quot;</code></pre><h1 id="conclusion">Conclusion</h1><p>This is the way to build your own docker image. Every time you have new changes you can run again: <code>npm run docker-build</code>.</p><p>And if you have Docker Hub you can push it with: <code>npm run docker:push</code>.</p><blockquote>Also don&#x2019;t forget to commit your git.</blockquote><p>Let me know what you think of this article?</p>]]></content:encoded></item><item><title><![CDATA[Say NO to TypeScript]]></title><description><![CDATA[<p>Ah I&#x2019;m just so done with it.</p><p>Stuff comes from Angular 2.0, then some people over-hype it, the community are just like sheep, polluting Vue 3.</p><p>Coming from a person that mastered JavaScript, I don&#x2019;t need or looking for jobs with TypeScript. Some people have</p>]]></description><link>https://blog.sylo.space/say-no-to-typescript/</link><guid isPermaLink="false">65d750b880008f00011debfc</guid><category><![CDATA[Journal]]></category><dc:creator><![CDATA[Harianto van Insulinde]]></dc:creator><pubDate>Thu, 22 Feb 2024 14:00:33 GMT</pubDate><content:encoded><![CDATA[<p>Ah I&#x2019;m just so done with it.</p><p>Stuff comes from Angular 2.0, then some people over-hype it, the community are just like sheep, polluting Vue 3.</p><p>Coming from a person that mastered JavaScript, I don&#x2019;t need or looking for jobs with TypeScript. Some people have written better articles, why it is so bad. (It&#x2019;s like they read my mind).</p>]]></content:encoded></item><item><title><![CDATA[Created `EventBus` library]]></title><description><![CDATA[I’ve created small library that I use for Vue 3 apps]]></description><link>https://blog.sylo.space/created-eventbus-library/</link><guid isPermaLink="false">656da3dc80008f00011de90f</guid><category><![CDATA[javascript]]></category><category><![CDATA[Vue]]></category><dc:creator><![CDATA[Harianto van Insulinde]]></dc:creator><pubDate>Mon, 04 Dec 2023 11:27:52 GMT</pubDate><content:encoded><![CDATA[<p>I&#x2019;ve created small library that I use for Vue 3 apps</p><figure class="kg-card kg-code-card"><pre><code class="language-js">// Factory: createEventBus
const createEventBus = function () {
  const state = {
    listeners: {}
  }

  const methods = {
    // addEventListener
    $on(eventType, callback) {
      if (!Array.isArray(state.listeners[eventType])) {
        state.listeners[eventType] = []
      }
      if (state.listeners[eventType].indexOf(callback) === -1)
        state.listeners[eventType].push(callback)
    },
    // removeEventListener
    $off(eventType, callback) {
      if (Array.isArray(state.listeners[eventType])) {
        const index = state.listeners[eventType].indexOf(callback)
        if (index !== -1) state.listeners[eventType].splice(index, 1)
      }
    },
    // dispatchEvent
    $emit(eventType, data) {
      if (Array.isArray(state.listeners[eventType]))
        state.listeners[eventType].forEach(cb =&gt; cb(data))
    },
    // reset listeners
    $destroy() {
      state.listeners = {}
    }
  }

  return methods
}

// Class: EventBus
const EventBus = function () {
  Object.assign(this, createEventBus())
}

export default createEventBus
export { EventBus }
</code></pre><figcaption>createEventBus.js</figcaption></figure><h1 id="how-to-use">How to use</h1><h2 id="factory-way">Factory way</h2><figure class="kg-card kg-code-card"><pre><code class="language-js">import createEventBus from &apos;./createEventBus&apos;

// create the product (from Factory)
const eventBus = createEventBus()
export default eventBus</code></pre><figcaption>eventBus.js</figcaption></figure><pre><code class="language-js">import eventBus from &apos;./eventBus&apos;

// # addEventListener
eventBus.$on(&apos;message&apos;, console.log.bind(console, &apos;message:&apos;))

const temporaryEvent = console.log.bind(console, &apos;temporary:&apos;)
eventBus.$on(&apos;temporary&apos;, temporaryEvent)

// # removeEventListener
eventBus.$off(&apos;temporary&apos;, temporaryEvent)

// # dispatchEvent
eventBus.$emit(&apos;message&apos;, &apos;Hello world&apos;)
// -&gt; message: Hello world
eventBus.$emit(&apos;message&apos;, { name: &apos;Anna&apos;, age: 21 })
// -&gt; {name: &apos;Anna&apos;, age: 21}

// # destroy events
eventBus.$destroy()

eventBus.$emit(&apos;message&apos;, &apos;This should not log after destroy&apos;)
// -&gt;           </code></pre><h2 id="class-way">Class way</h2><figure class="kg-card kg-code-card"><pre><code class="language-js">import { EventBus } from &apos;./createEventBus&apos;

// instantiate EventBus
const eventBus = new EventBus()
export default eventBus</code></pre><figcaption>eventBus.js</figcaption></figure><pre><code class="language-js">import { EventBus } from &apos;./createEventBus&apos;
import eventBus from &apos;./eventBus&apos;

// # Check instance
console.log(eventBus instanceof EventBus)
// -&gt; true

// # addEventListener
eventBus.$on(&apos;message&apos;, console.log.bind(console, &apos;message:&apos;))

const temporaryEvent = console.log.bind(console, &apos;temporary:&apos;)
eventBus.$on(&apos;temporary&apos;, temporaryEvent)

// # removeEventListener
eventBus.$off(&apos;temporary&apos;, temporaryEvent)

// # dispatchEvent
eventBus.$emit(&apos;message&apos;, &apos;Hello world&apos;)
// -&gt; message: Hello world
eventBus.$emit(&apos;message&apos;, { name: &apos;Anna&apos;, age: 21 })
// -&gt; {name: &apos;Anna&apos;, age: 21}


// # destroy events
eventBus.$destroy()

eventBus.$emit(&apos;message&apos;, &apos;This should not log after destroy&apos;)
// -&gt;           </code></pre><h2 id="vue-option-api-way">Vue Option API way</h2><blockquote>No, I don&#x2019;t like over-hyped Composition API</blockquote><figure class="kg-card kg-code-card"><pre><code class="language-html">&lt;script&gt;
import createEventBus from &apos;./createEventBus&apos;

export default {
  // LifeCycle Hooks
  beforeCreate() {
    // create the product (from Factory)
    const eventBus = eventEventBus()
    const {
      $on: $$on,
      $off: $$off,
      $emit: $$emit // Vue has internal $emit, change to $$emit
    } = eventBus
    // assign to Proxy Object
    Object.assign(
      this,
      { eventBus },
      {
        $$on,
        $$off,
        $$emit
      }
    )
  },
  mounted() {
    this.intervalID = setInterval(() =&gt; {
      this.$$emit(&apos;tick&apos;, &apos;Tick Tock&apos;)
    }, 1e3)
  },
  beforeUnmount() {
    // destroy IntervalID 
    clearInterval(this.intervalID)
    // destroyes all eventBus event listeners
    this.eventBus.$destroy()   
  }
}
&lt;/script&gt;</code></pre><figcaption>HelloWorld.vue</figcaption></figure><pre><code class="language-js">import { createApp } form &apos;vue&apos;
import HelloWorld from &apos;./HelloWorld.vue&apos;

const template = document.createElement(&apos;template&apos;)
const app = createApp(vueOjbject, props)
const proxy = app.mount(template) // Proxy Object

// attach addEventListener
proxy.$$on(&apos;tick&apos;, data =&gt; {
  console.log(&apos;on tick:&apos;, data)
})
// -&gt; on tick: Tick Tock
// -&gt; on tick: Tick Tock
// -&gt; on tick: Tick Tock

</code></pre>]]></content:encoded></item><item><title><![CDATA[Revise my code from Vue 2 to Vue 3]]></title><description><![CDATA[Try to understand Vue 3 Advanced things what I’ve known from Vue 2. And migrate some of my codes]]></description><link>https://blog.sylo.space/revise-my-code-from-vue-2-to-vue-3/</link><guid isPermaLink="false">656847bc80008f00011de707</guid><category><![CDATA[Vue]]></category><category><![CDATA[Front end]]></category><category><![CDATA[javascript]]></category><dc:creator><![CDATA[Harianto van Insulinde]]></dc:creator><pubDate>Fri, 01 Dec 2023 17:01:13 GMT</pubDate><content:encoded><![CDATA[<p>Vue 2.7.15<br>Vue 3.3.4<br>Vite 4.4.5<br>Node 16.14.0<br>NPM 8.3.1</p><h1 id="%60vueextend%60-to-%60createapp%60">`Vue.extend` to `createApp`</h1><p>In <strong>Vue 2</strong> if you want to extend a component Types.vue</p><figure class="kg-card kg-code-card"><pre><code class="language-js">import Vue from &apos;vue&apos;
import Types from &apos;./Types.vue&apos;

const methods = {
  extendedTypes({ type, title, message }) {
    const ExtendedVue = Vue.extend(Types)
    const vm = new ExtendedVue({
      propsData: {
        type,
        title,
        message,
      },
    }).$mount()
    return vm
  }
}

// vm (aka Proxy Object)
const proxy = methods.extendedTyes(&apos;default&apos;, &apos;Hello&apos;, &apos;World&apos;)
const node = proxy.$el</code></pre><figcaption>Vue 2: Vue.extend</figcaption></figure><blockquote><code>proxy.$el</code> in Vue 2 restricts to one child element. When you append a child to a <code>parentNode</code>, would be easy: <code>parentNode.appendChild(proxy.$el)</code></blockquote><p>to <strong>Vue 3</strong></p><figure class="kg-card kg-code-card"><pre><code class="language-js">import { createApp } from &apos;vue&apos;
import Types from &apos;./Types.vue&apos;

const extend = (
  vueOjbject = { template: `&lt;span&gt;test&lt;/span&gt;` },
  props = {}
) =&gt; {
  const template = document.createElement(&apos;template&apos;)
  const app = createApp(vueOjbject, props)
  return app.mount(template) // Proxy Object
}

const methods = {
  extendedTypes({ type, title, message }) {
    return extend(Types, { type, title, message })
  }
}

// vm (aka Proxy Object)
const proxy = methods.extendedTyes(&apos;default&apos;, &apos;Hello&apos;, &apos;World&apos;)
const node = proxy.$el</code></pre><figcaption>Vue 3: createApp</figcaption></figure><blockquote><code>proxy.$el</code> in Vue 3, has <code>HTMLElement</code> (single child) or <code>Text</code> (multiple children). Would appending <code>proxy.$el</code> more challenging.</blockquote><figure class="kg-card kg-code-card"><pre><code class="language-js">const appendChild = (parentNode, childNode) =&gt; {
  console.log(`-------# constructor: ${childNode?.constructor?.name}`)
 
  switch (true) {
    case childNode instanceof HTMLTemplateElement:
      console.log(&apos;---- HTMLTemplateElement&apos;)
      Array.from(childNode.childNodes).forEach(child =&gt;
        parentNode.appendChild(child)
      )
      break

    case childNode instanceof Text:
      console.log(&apos;---- Text: Proxy.$el&apos;)
      appendChild(parentNode, childNode.parentNode)
      break

    case childNode instanceof HTMLElement:
      console.log(&apos;---- HTMLElement&apos;)
      parentNode.appendChild(childNode)
      break

    default:
      console.log(&apos;---- nothing&apos;)
      break
  }
}</code></pre><figcaption>appendChild</figcaption></figure><pre><code class="language-js">const parentNode = document.createElement(&apos;div&apos;)
// Add Extended Vue Module to parentNode
appendChild(parentNode, proxy.$el)</code></pre><h1 id="advanced-mounting">Advanced mounting</h1><blockquote>Use <code>&apos;vue/dist/vue.esm-bundler&apos;</code> instead of <code>&apos;vue&apos;</code></blockquote><figure class="kg-card kg-code-card"><pre><code class="language-js">import { defineComponent, createApp, reactive } from &apos;vue/dist/vue.esm-bundler&apos;

const template = document.createElement(&apos;template&apos;)
const component = defineComponent({
  template: `&lt;h1 v-text=&quot;title&quot;/&gt;
    &lt;p v-text=&quot;description&quot; /&gt;
    &lt;button @click=&quot;onClick&quot;&gt;{{ buttonName }}&lt;/button&gt;`,
  props: [&apos;title&apos;]},
  data: () =&gt; ({
    buttonName: &apos;Click,
    description: &apos;&apos;
  }),
  methods: {
    onClick() {
      // Use `proxy` aka `vm`
      if (typeof this.clicked === &apos;function&apos;) this.clicked(this)
    }
  }
)
const app = createApp(component, { title: &apos;Master of Code&apos; })

// kind of `vm` instance after `mount`
const proxy = app.mount(template)

// controll the Vue Module
// --- attach `clicked` function
proxy.clicked = vm =&gt; {
  vm.buttonName = &apos;Clicked&apos;
  alert(&apos;Button is clicked&apos;)
}


// appendChild to a element `parentNode`
const vInjectElements = () =&gt; {
  const log = console.log
  let logText = &apos;/root&apos;
  const appendChild = (parentNode, childNode) =&gt; {
    log(`-- # constructor: ${childNode?.constructor?.name}`)
    logText += &apos;.constructor&apos;
    let template
    switch (true) {
      case childNode instanceof HTMLTemplateElement:
        log(&apos;---- HTMLTemplateElement&apos;)
        logText += &apos;.HTMLTemplateElement&apos;
        log(logText)
        Array.from(childNode.childNodes).forEach(child =&gt;
          parentNode.appendChild(child)
        )
        break

      case childNode instanceof Text:
        log(&apos;---- Text: Proxy.$el&apos;)
        logText += &apos;.Text&apos;
        // template Element from childNode.parentNode
        appendChild(parentNode, childNode.parentNode)
        break

      case childNode instanceof DocumentFragment:
        log(&apos;---- DocumentFragment&apos;)
        logText += &apos;.DocumentFragment&apos;
        log(logText)
        parentNode.appendChild(childNode)
        break

      case childNode instanceof HTMLElement:
        log(&apos;---- HTMLElement&apos;)
        logText += &apos;.HTMLElement&apos;
        log(logText)
        parentNode.appendChild(childNode)
        break

      case childNode instanceof Object:
        log(&apos;---- Object Vue Component&apos;)
        template = document.createElement(&apos;template&apos;)
        logText += &apos;.VueComponent&apos;
        // Check if object is a Vue App or Vue Component
        if (typeof childNode.mount === &apos;function&apos;) {
          // Vue App
          log(&apos;------ by: createApp&apos;)
          logText += &apos;.createApp&apos;
          if (childNode._container) {
            log(&apos;-------- is already mounted&apos;)
            logText += &apos;._container&apos;
            template = childNode._container
          } else {
            log(&apos;-------- mount the childNode&apos;)
            logText += &apos;.mountChildNodeToTemplate&apos;
            childNode.mount(template)
          }
        } else {
          // Vue Component
          log(&apos;------ by: defineComponent&apos;)
          logText += &apos;.defineComponent&apos;
          if (childNode?.$?.isMounted) {
            logText += &apos;.$el&apos;
            template = childNode.$el
          } else {
            logText += &apos;.createAppAndMount&apos;
            createApp(childNode).mount(template)
          }
        }
        appendChild(parentNode, template)
        break

      default:
        log(&apos;---- nothing&apos;)
        logText += &apos;.nothing&apos;
        log(logText)
        break
    }
  }
  
  return {
    mounted(el, binding) {
      const { value: elements } = binding
      elements.forEach(childNode =&gt; appendChild(el, childNode))
    }
  }
}

export default {
  directives: {
    injectElements: vInjectElements
  },
  data: () =&gt; ({
    list: []
  }),
  template: `&lt;h1&gt;Title&lt;/h1&gt;
  &lt;p&gt;Some paragraph here&lt;/p&gt;
  &lt;section v-inject-elements=&quot;list&quot; /&gt;`
}</code></pre><figcaption>HelloWorld.vue</figcaption></figure>]]></content:encoded></item><item><title><![CDATA[Getting Accessories for MacBook Air (M1, 2020)]]></title><description><![CDATA[<p></p><p>The products I bought that seems to help for my needs.</p><h1 id="bringing-back-that-macsafe">Bringing back that MacSafe</h1><p>MacSafe is a magnetic technology from Apple they used to had on older MacBook models; a charging port that connect with cable with magnets. This would prevent accidents when someone walk over your cable but</p>]]></description><link>https://blog.sylo.space/getting-accessories-for-macbook-air-m1-2020/</link><guid isPermaLink="false">622e7031d59c6e00019a6a7f</guid><category><![CDATA[Journal]]></category><category><![CDATA[Draft]]></category><dc:creator><![CDATA[Harianto van Insulinde]]></dc:creator><pubDate>Mon, 14 Mar 2022 01:01:42 GMT</pubDate><content:encoded><![CDATA[<p></p><p>The products I bought that seems to help for my needs.</p><h1 id="bringing-back-that-macsafe">Bringing back that MacSafe</h1><p>MacSafe is a magnetic technology from Apple they used to had on older MacBook models; a charging port that connect with cable with magnets. This would prevent accidents when someone walk over your cable but get caught and swoop your laptop off the table.</p><p>This MacBook only comes with 2 Thunderbolt/USB 4 and headphone ports.</p><p>So I thought maybe this year there is something for it and I found two products and keys that I need:</p><ul><li>It has magnets</li><li>4K at least 60Hz</li><li>Charge my MacBook</li><li>Transfer data</li></ul><blockquote>Basically everything, but and extension.</blockquote><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://blog.sylo.space/content/images/2022/03/Digifunk---USB-C-Magnetic-Adapter---X001FSDHKN.jpeg" width="1224" height="1632" loading="lazy" alt srcset="https://blog.sylo.space/content/images/size/w600/2022/03/Digifunk---USB-C-Magnetic-Adapter---X001FSDHKN.jpeg 600w, https://blog.sylo.space/content/images/size/w1000/2022/03/Digifunk---USB-C-Magnetic-Adapter---X001FSDHKN.jpeg 1000w, https://blog.sylo.space/content/images/2022/03/Digifunk---USB-C-Magnetic-Adapter---X001FSDHKN.jpeg 1224w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://blog.sylo.space/content/images/2022/03/Digifunk.jpg" width="1500" height="1059" loading="lazy" alt srcset="https://blog.sylo.space/content/images/size/w600/2022/03/Digifunk.jpg 600w, https://blog.sylo.space/content/images/size/w1000/2022/03/Digifunk.jpg 1000w, https://blog.sylo.space/content/images/2022/03/Digifunk.jpg 1500w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>USB C Magnet Adapter | Thunderbolt 3 | 100W PD Fast Charge | 20Gb/s Data Transfer | 4K @ 60Hz Video Output | Magsafe Angle Plug</figcaption></figure><blockquote>It&apos;s small and compact and you can easily fit two of these in your MacBook. But the magnet feels weak against sturdy USB-C cables.</blockquote><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://blog.sylo.space/content/images/2022/03/iSkey---connectors.jpeg" width="1414" height="1414" loading="lazy" alt srcset="https://blog.sylo.space/content/images/size/w600/2022/03/iSkey---connectors.jpeg 600w, https://blog.sylo.space/content/images/size/w1000/2022/03/iSkey---connectors.jpeg 1000w, https://blog.sylo.space/content/images/2022/03/iSkey---connectors.jpeg 1414w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://blog.sylo.space/content/images/2022/03/iSkey.jpg" width="833" height="1000" loading="lazy" alt srcset="https://blog.sylo.space/content/images/size/w600/2022/03/iSkey.jpg 600w, https://blog.sylo.space/content/images/2022/03/iSkey.jpg 833w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>USB C Magnetic Adapter 20 Pins Type C Connector, Supports USB pd 100 W Fast Charge, 10 GBP/s Data Transfer and 4K @ 60 Hz Video Output, Compatible with MacBook Pro/Air and Other Type C Devices</figcaption></figure><blockquote>I personally use this more often, because I&apos;ve got sturdy USB-C cable and this product has Ultra Strong Magnets. But it&apos;s quite big and can&apos;t only fit one of the same.</blockquote><h1 id="closing-the-lid-scratches-the-screen">Closing the lid scratches the screen</h1><p>Closing the lid makes contact with keyboard keys that scratches the screen overtime. I&apos;ve used many MacBook&apos;s over the years and even I use it carefully I still get scratches on my screen. You can guess it how; It&#x2019;s the keyboard!</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2022/03/LetsSwipeThat---13inch-cloth.jpg" class="kg-image" alt loading="lazy" width="1436" height="1039" srcset="https://blog.sylo.space/content/images/size/w600/2022/03/LetsSwipeThat---13inch-cloth.jpg 600w, https://blog.sylo.space/content/images/size/w1000/2022/03/LetsSwipeThat---13inch-cloth.jpg 1000w, https://blog.sylo.space/content/images/2022/03/LetsSwipeThat---13inch-cloth.jpg 1436w" sizes="(min-width: 720px) 720px"><figcaption>3X LetsSwipeThat microvezeldoeken - 13 inch microvezel scherm beschermdoek. Microvezeldoek voor bescherming tegen vuil op het laptop toetsenbord. Microvezeldoek voor notebookreiniging</figcaption></figure><blockquote>I can finally close the lid with this microfibre cloth between it.</blockquote><h1 id="usb-c-to-usb-c-cable">USB-C to USB-C cable</h1><p>Connect my MacBook to an external monitor. What is with this USB-C to HDMI cable/adapter that can&#x2019;t deliver 4K @ 60Hz. I thought I found an adapter, but it never supports 3840 x 2160. And all those cables extensions really look-a-like that you really need to check carefully for not buying the wrong product.</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://blog.sylo.space/content/images/2022/03/Nimaso---USB-C.jpg" width="1500" height="1469" loading="lazy" alt srcset="https://blog.sylo.space/content/images/size/w600/2022/03/Nimaso---USB-C.jpg 600w, https://blog.sylo.space/content/images/size/w1000/2022/03/Nimaso---USB-C.jpg 1000w, https://blog.sylo.space/content/images/2022/03/Nimaso---USB-C.jpg 1500w" sizes="(min-width: 1200px) 1200px"></div></div></div><figcaption>NIMASO USB C to USB C 3.1 Gen2 Cable, PD 100W USB Type C to Type C Fast Charging Cable 4K Video Output for Samsung GalaxyS20 Ultra/Note 10 Macbook Pro, iPad Pro 2020/2018, HUAWEI MateBook</figcaption></figure><blockquote>Maybe I should buy 3 meter long cable, or a Thunderbolt could be good idea. Might be overkill.</blockquote>]]></content:encoded></item><item><title><![CDATA[Setting up `MacBook Air M1 2020` in 2022]]></title><description><![CDATA[<p>It&#x2019;s new year 2022 and you decide to upgrade your system and this time you want to be fan-less, and the promising M1 Silicon MacBook Air will hopefully do the job for your career as a Frontend Developer. Also you have bought a Synology NAS treating your Air</p>]]></description><link>https://blog.sylo.space/setting-up-macbook-air-m1-2020-in-2022/</link><guid isPermaLink="false">621b59a4140bec0001a0cae9</guid><category><![CDATA[Journal]]></category><category><![CDATA[Installations]]></category><dc:creator><![CDATA[Harianto van Insulinde]]></dc:creator><pubDate>Sun, 27 Feb 2022 15:05:01 GMT</pubDate><content:encoded><![CDATA[<p>It&#x2019;s new year 2022 and you decide to upgrade your system and this time you want to be fan-less, and the promising M1 Silicon MacBook Air will hopefully do the job for your career as a Frontend Developer. Also you have bought a Synology NAS treating your Air (as MacBook Air M1) as a to-go portable work station. Also you eagerly waiting for the new M2 so you could easily replace.</p><p>When my &#x2018;Toy&#x2019; arrived I felt like a little kid, things I want to do with it comes to mind.</p><h1 id="start-your-m1-mac-up-in-recovery-mode">Start Your M1 Mac Up in Recovery Mode</h1><p>New Mac OS installation comes as Case-Insensitive and as a developer I felt like I need to turn back to Case-Sensitive. So I have to format the hard-drive as a APFS Case-Sensitive.</p><!--kg-card-begin: html--><p>To do this, you have to complete shutdown your Air. Then press down and hold the <kbd>Power Button</kbd></p>

<p>Press and hold the <kbd>Power&#xA0;Button</kbd> until you see &#x2018;loading startup options&#x2019; under Apple logo. Select Options and click Continue. Choose an administrator account and enter its password.</p><!--kg-card-end: html--><h2 id="format-hard-disk-as-apfs-case-sensitive">Format Hard Disk as APFS (Case-Sensitive)</h2><ul><li>Choose &#x2018;Disk Utility&#x2019; and click Continue.</li><li>On &#x2018;Internal&#x2019; choose &#x2018;Macintosh HD volumes&#x2019;.</li><li>On right top menu buttons choose &#x2018;Erase&#x2019;.</li></ul><!--kg-card-begin: markdown--><ul>
<li>A new window pop-up &#x2018;Erase APFS volume group &#x201C;Macintosh HD&#x201D;?&#x2019;
<ul>
<li>Name: <code>Macintosh HD</code></li>
<li>Format: <code>APFS (Case-sensitive)</code></li>
<li>Then press <kbd>Erase</kbd></li>
</ul>
</li>
</ul>
<!--kg-card-end: markdown--><blockquote>After formatting Recovery Mode will automatically reboot system.</blockquote><h1 id="update-your-os">Update your OS</h1><!--kg-card-begin: markdown--><ul>
<li>On the apple icon (&#xF8FF;) go to: About This Mac</li>
<li>An Overview will shown then press: <kbd>Software Update</kbd></li>
</ul>
<!--kg-card-end: markdown--><h2 id="install-command-line-tools">Install Command Line Tools</h2><pre><code class="language-bash">xcode-select --install</code></pre><h1 id="generating-a-new-ssh-key">Generating a new SSH key</h1><figure class="kg-card kg-code-card"><pre><code class="language-bash">ssh-keygen -t ed25519</code></pre><figcaption>This creates a new SSH key, using the system user and system name</figcaption></figure><blockquote>More info find here: <a href="https://docs.github.com/en/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent">Generating a new SSH key</a></blockquote><pre><code class="language-bash"># view .pub file
cat ~/.ssh/id_ed25519.pub

# copy file to clipboard
pbcopy &lt; ~/.ssh/id_ed25519.pub

# check content from clipboard
pbpaste</code></pre><h2 id="optional">Optional</h2><p>In <a href="https://github.com/settings/keys">GitHub</a> add New SSH key</p><h1 id="install-system-softwares">Install system softwares</h1><ul><li><a href="https://iterm2.com/downloads.html">iTerm2</a></li><li><a href="https://ohmyz.sh/#install">oh-my-zsh</a></li><li><a href="https://brew.sh/">brew</a> (to install NVM)<br>- <code>brew install nvm</code><br>- <code>nvm install 16</code></li><li><a href="https://docs.docker.com/desktop/mac/install/">Docker Desktop on Mac with Apple chip</a></li></ul><h1 id="install-apps">Install Apps</h1><ul><li><a href="https://github.com/HariantoAtWork/docker-xnmp-vhosts.git">docker-xnmp-vhost</a></li><li><a href="https://code.visualstudio.com/download">Visual Studio Code</a></li></ul><h2 id="optional-apps">Optional Apps</h2><ul><li><a href="https://apps.apple.com/nl/app/bitwarden/id1352778147?l=en&amp;mt=12">Bitwarden</a></li><li><a href="https://apps.apple.com/nl/app/display-menu/id549083868?l=en&amp;mt=12">Display Menu</a></li><li><a href="https://github.com/MonitorControl/MonitorControl">MonitorControl</a></li><li><a href="https://apps.apple.com/nl/app/moom/id419330170?l=en&amp;mt=12">Moom</a></li><li><a href="https://apps.apple.com/nl/app/menubarx/id1575588022?l=en&amp;mt=12">MenubarX</a></li><li><a href="https://apps.apple.com/nl/app/forklift-file-manager-and-ftp-sftp-webdav-amazon-s3-client/id412448059?l=en&amp;mt=12">Forklift</a></li><li><a href="https://www.synology.com/en-uk/support/download/DS920+?version=7.0#utilities">Synology Drive Client 920+</a></li></ul><h1 id="configurations">Configurations</h1><h2 id="zsh">ZSH</h2><p>Append some lines</p><figure class="kg-card kg-code-card"><pre><code class="language-bash"># Connect VPS
alias vps=&apos;ssh root@xxx.xxx.xxx.xx&apos;

# Docker commands
alias docker.kill=&apos;docker rm -f $(docker ps -a -q)&apos;
alias docker.dangling=&apos;docker images -q --filter &quot;dangling=true&quot;&apos;
alias docker.remove-untagged-images=&apos;docker rmi -f `docker.dangling` &gt; /dev/null 2&gt;&amp;1 || echo &quot;Nothing to remove&quot;&apos;
alias docker.ip=&apos;docker inspect -f &quot;{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}&quot; $1&apos;
alias docker.down=&apos;docker-compose down --remove-orphans&apos;
alias docker.up=&apos;docker-compose up --build -d&apos;
alias docker.restart=&apos;docker.down &amp;&amp; docker.up &amp;&amp; docker-compose logs -f&apos;

# NVM config
export NVM_DIR=&quot;$HOME/.nvm&quot;
[ -s &quot;/opt/homebrew/opt/nvm/nvm.sh&quot; ] &amp;&amp; \. &quot;/opt/homebrew/opt/nvm/nvm.sh&quot;  # This loads nvm
[ -s &quot;/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm&quot; ] &amp;&amp; \. &quot;/opt/homebrew/opt/nvm/etc/bash_completion.d/nvm&quot;  # This loads nvm bash_completion</code></pre><figcaption>File: ~/.zshrc</figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-bash">eval &quot;$(/opt/homebrew/bin/brew shellenv)&quot;</code></pre><figcaption>File: ~/.zprofile</figcaption></figure><h2 id="iterm2">iTerm2</h2><p>Assign a Hotkey to bring terminal with ease.</p><h3 id="setting-hotkey">Setting Hotkey</h3><ul><li>In Preferences go to Keys / HotKey / Create a Dedicated Hotkey Window, then a new window appears.</li><li>Activate <em>Double-tap Key</em>: and set to CMD, and OK.</li><li>Activate <em>Pin hotkey window.</em></li><li>Activate <em>Animate showing and hiding</em>.</li></ul><h2 id="display-menu">Display Menu</h2><p>Sometimes you just want the full resolution (2560x1600) instead of retina (1680x1050) as workspace. It&apos;s easier to switch resolution between monitors.</p><h2 id="monitorcontrol">MonitorControl</h2><p>Strangely MacBook only work with single Master Volume and I really want to control volume on my external monitor. At least this will work for me.</p><h2 id="moom">Moom</h2><p>Working with external 4K monitor, I want 3 columns window instead of 2. This is good enough, but I rather have those features on my PopOS Gnome Extension called gTile. Using Hotkeys combo to bring up a panel then reorder all your windows.</p><h2 id="menubarx">MenubarX</h2><p>Side load a mini browser for live stats.</p><h2 id="forklift">Forklift</h2><p>It&#x2019;s a two-panel Finder to manage your files.</p><h2 id="synology-drive-client">Synology Drive Client</h2><p>It&#x2019;s like a DropBox but private.</p><h1 id="macbook-accessories">MacBook Accessories</h1><ul><li>LetsSwipeThat - cloth; between keys and screen, mainly to protect the screen when closing the lid.</li><li>Magnetic USB-C Adapter 4K - bring back MacSafe</li></ul><h1 id="ideas">Ideas</h1><ul><li><a href="https://sftptogo.com/blog/how-to-mount-sftp-as-a-drive-on-mac/">How to mount SFTP as a drive on Mac</a></li><li><a href="https://techstuffer.com/fuse-for-macos-apple-silicon-m1/">Making FUSE for macOS Work with Apple Silicon (M1) Macs</a></li></ul><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[Use NVME SSD as storage volume instead of cache in DS920+]]></title><description><![CDATA[<!--kg-card-begin: html--><style>
code.language-bash .line-numbers-rows span::before {
    content: "";
}
code.language-bash .line-numbers-rows span:first-child::before {
	content: "$";    
}
</style><!--kg-card-end: html--><blockquote>Synology: DSM 7.2.1-69057 Update 3</blockquote><blockquote>For those who had errors after reboot. The script that update the DSM devices works for me.</blockquote><p>When Synology released DS920+, many customer (include me) think the additional NVME slots</p>]]></description><link>https://blog.sylo.space/use-nvme-ssd-as-storage-volume-instead-of-cache-in-ds920/</link><guid isPermaLink="false">61fec223140bec0001a0c9a3</guid><category><![CDATA[Synology]]></category><category><![CDATA[DSM]]></category><dc:creator><![CDATA[Harianto van Insulinde]]></dc:creator><pubDate>Tue, 08 Feb 2022 12:59:42 GMT</pubDate><content:encoded><![CDATA[<!--kg-card-begin: html--><style>
code.language-bash .line-numbers-rows span::before {
    content: "";
}
code.language-bash .line-numbers-rows span:first-child::before {
	content: "$";    
}
</style><!--kg-card-end: html--><blockquote>Synology: DSM 7.2.1-69057 Update 3</blockquote><blockquote>For those who had errors after reboot. The script that update the DSM devices works for me.</blockquote><p>When Synology released DS920+, many customer (include me) think the additional NVME slots can use as storage. But the truth is - NVME SSD can only setup as cache in DSM.</p><p>As DSM is a Linux based system, I think I can try to use command (instead of DSM interface) setup NVME as storage. After some trial and test, I found the step to make it work.</p><blockquote><strong><strong>You should have some knowledge on using command line (ssh) in DSM and highly recommend to backup you system beforehand. Also, you may risk to lost your DSM setting/volume/data.</strong></strong></blockquote><h1 id="install-nvme-ssd">Install NVMe SSD</h1><p>Install NVME SSD to DS920 and bootup. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager-Overview.png" class="kg-image" alt loading="lazy" width="1280" height="671" srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Synology-Storage-Manager-Overview.png 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Synology-Storage-Manager-Overview.png 1000w, https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager-Overview.png 1280w" sizes="(min-width: 720px) 720px"><figcaption>Storage Manager: Overview; Slot 1 looks transparent in Build-In M.2 Slot</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager--1.png" class="kg-image" alt loading="lazy" width="1279" height="485" srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Synology-Storage-Manager--1.png 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Synology-Storage-Manager--1.png 1000w, https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager--1.png 1279w" sizes="(min-width: 720px) 720px"><figcaption>Storage Manager: Showing installed NVME as Cache device 1</figcaption></figure><h1 id="enable-ssh">Enable SSH</h1><blockquote>If you already enabled SSH. You can skip this.</blockquote><p>Enable SSH login on your DS920+.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2024/02/Synology-Control-Panel-Terminal-Enable-SSH-Service.jpg" class="kg-image" alt loading="lazy" width="1280" height="666" srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Synology-Control-Panel-Terminal-Enable-SSH-Service.jpg 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Synology-Control-Panel-Terminal-Enable-SSH-Service.jpg 1000w, https://blog.sylo.space/content/images/2024/02/Synology-Control-Panel-Terminal-Enable-SSH-Service.jpg 1280w" sizes="(min-width: 720px) 720px"><figcaption>Control Panel / Terminal &amp; SNMP / Terminal: Enable SSH service</figcaption></figure><h1 id="use-script-to-add-your-drives-to-your-synologys-drive-compatibility-database-v1216">Use script to add your drives to your Synology&apos;s drive compatibility database v1.2.16</h1><p>I found some sources to fix my device by running a script. Read more below how I fixed my NVMe device.</p><p>Login to SSH as root ( <code>sudo -s</code> )</p><figure class="kg-card kg-code-card"><pre><code class="language-bash"># create directory
mkdir -p /volume1/scripts
# goto directory
cd /volume1/scripts
# download file from github and untar
curl -sSL https://github.com/007revad/Synology_HDD_db/archive/master.tar.gz | tar -xvzf -</code></pre><figcaption>Extracts to: <code>Synology_HDD_db-main/</code></figcaption></figure><figure class="kg-card kg-code-card"><pre><code>Synology_HDD_db-main/
Synology_HDD_db-main/.github/
Synology_HDD_db-main/.github/FUNDING.yml
Synology_HDD_db-main/CHANGES.txt
Synology_HDD_db-main/LICENSE
Synology_HDD_db-main/README.md
Synology_HDD_db-main/bin/
Synology_HDD_db-main/bin/dtc
Synology_HDD_db-main/how_to_schedule.md
Synology_HDD_db-main/images/
Synology_HDD_db-main/images/how_to_download.png
Synology_HDD_db-main/images/ram_warning.png
Synology_HDD_db-main/images/schedule1.png
Synology_HDD_db-main/images/schedule2.png
Synology_HDD_db-main/images/schedule3.png
Synology_HDD_db-main/images/syno_hdd_db.png
Synology_HDD_db-main/images/syno_hdd_db1.png
Synology_HDD_db-main/images/syno_hdd_db2.png
Synology_HDD_db-main/images/syno_hdd_db_help.png
Synology_HDD_db-main/images/syno_hdd_db_help2.png
Synology_HDD_db-main/images/unknown.png
Synology_HDD_db-main/images/update-now-disabled.png
Synology_HDD_db-main/images/update-now-working.png
Synology_HDD_db-main/images/vendor_ids.png
Synology_HDD_db-main/my-other-scripts.md
Synology_HDD_db-main/syno_hdd_db.sh
Synology_HDD_db-main/syno_hdd_vendor_ids.txt</code></pre><figcaption><code>chmod +x Synology_HDD_db-main/syno_hdd_db.sh</code></figcaption></figure><h2 id="run-the-script">Run the script</h2><pre><code class="language-bash"># CHMOD the file
chmod +x Synology_HDD_db-main/syno_hdd_db.sh

# Run the script
/volume1/scripts/Synology_HDD_db-main/syno_hdd_db.sh</code></pre><figure class="kg-card kg-code-card"><pre><code>Synology_HDD_db v3.4.84
DS920+ DSM 7.2.1-69057-3
StorageManager 1.0.0-0017

Using options:
Running from: /volume1/scripts/Synology_HDD_db-main/syno_hdd_db.sh

HDD/SSD models found: 1
ST16000NE000-2RW103,EN02

M.2 drive models found: 1
Samsung SSD 980 PRO 1TB,4B2QGXA7

No M.2 PCIe cards found

No Expansion Units found

ST16000NE000-2RW103 already exists in ds920+_host_v7.db
Edited unverified drives in ds920+_host_v7.db
Added Samsung SSD 980 PRO 1TB to ds920+_host_v7.db

Backed up synoinfo.conf

Support disk compatibility already enabled.

Support memory compatibility already enabled.

NVMe support already enabled.

Enabled M.2 volume support.

Drive db auto updates already enabled.

DSM successfully checked disk compatibility.

You may need to reboot the Synology to see the changes.</code></pre><figcaption>After running: <code>/volume1/scripts/Synology_HDD_db-main/syno_hdd_db.sh</code></figcaption></figure><blockquote>v3.4.84 this time of writing<br><a href="https://github.com/007revad/Synology_HDD_db/archive/refs/tags/v3.4.84.tar.gz">https://github.com/007revad/Synology_HDD_db/archive/refs/tags/v3.4.84.tar.gz</a></blockquote><h3 id="optional">Optional</h3><p>You can download the file from the Release instead of Main branch</p><figure class="kg-card kg-bookmark-card kg-card-hascaption"><a class="kg-bookmark-container" href="https://github.com/007revad/Synology_HDD_db/releases/latest"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Release v3.4.84 &#xB7; 007revad/Synology_HDD_db</div><div class="kg-bookmark-description">Bug fix when script updates itself and user ran the script from ./scriptname.sh</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">007revad</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/70190cad05a941d4ead21ce5cc2655813196334f8c94d55b63d2d6e6271e5937/007revad/Synology_HDD_db/releases/tag/v3.4.84" alt></div></a><figcaption>Get the latest release</figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-bash">curl -sSL `curl -sSL https://api.github.com/repos/007revad/Synology_HDD_db/releases/latest | jq -r &apos;.tarball_url&apos;` | tar -xvzf -</code></pre><figcaption>This will download the latest Release file and unpack</figcaption></figure><h2 id="optional-run-script-on-every-boot">Optional: Run script on every boot</h2><p>Yes. You also have to re-run the script after DSM downloads a newer version of the drive-compatibility database, which can happen between DSM updates.</p><p>It&apos;s best to schedule the script to run when the Synology boots.</p><ul><li>Go to Control Panel &gt; Task Scheduler, click Create, and select Triggered Task.</li><li>Select User-defined script.</li><li>Enter a task name.</li><li>Select root as the user.</li><li>Select Boot-up as the event that triggers the task.</li><li>Leave enable ticked.</li><li>Click Task Settings.</li><li>Optionally you can tick &quot;Send run details by email&quot; and &quot;Send run details only when the script terminates abnormally&quot; then enter your email address.</li><li>In the box under &quot;User-defined script&quot; type the path to the script. e.g. If you saved the script to a shared folder on volume1 called &quot;scripts&quot; you&apos;d type: <code>/volume1/scripts/Synology_HDD_db-main/syno_hdd_db.sh</code></li><li>Click OK to save the settings.</li></ul><h2 id="in-your-dsm-see-the-changes">In your DSM see the changes</h2><figure class="kg-card kg-image-card"><img src="https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---IM2-Drive-1.png" class="kg-image" alt loading="lazy" width="1280" height="485" srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Synology-Storage-Manager---IM2-Drive-1.png 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Synology-Storage-Manager---IM2-Drive-1.png 1000w, https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---IM2-Drive-1.png 1280w" sizes="(min-width: 720px) 720px"></figure><h2 id="sources-script-compatibility-database">Sources (Script compatibility database)</h2><figure class="kg-card kg-embed-card kg-card-hascaption"><blockquote class="reddit-embed-bq" style="height:316px">
<a href="https://www.reddit.com/r/synology/comments/11vyh13/script_to_add_your_drives_to_your_synologys_drive/">Script to add your drives to your Synology&apos;s drive compatibility database v1.2.16</a><br> by
<a href="https://www.reddit.com/user/DaveR007/">u/DaveR007</a> in
<a href="https://www.reddit.com/r/synology/">synology</a>
</blockquote>
<script async src="https://embed.reddit.com/widgets.js" charset="UTF-8"></script><figcaption>Reddit where it started. Amazing guy created the script</figcaption></figure><figure class="kg-card kg-bookmark-card kg-card-hascaption"><a class="kg-bookmark-container" href="https://github.com/007revad/Synology_HDD_db"><div class="kg-bookmark-content"><div class="kg-bookmark-title">GitHub - 007revad/Synology_HDD_db: Add your HDD, SSD and NVMe drives to your Synology&#x2019;s compatible drive database</div><div class="kg-bookmark-description">Add your HDD, SSD and NVMe drives to your Synology&amp;#39;s compatible drive database - GitHub - 007revad/Synology_HDD_db: Add your HDD, SSD and NVMe drives to your Synology&amp;#39;s compatible drive dat...</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.com/fluidicon.png" alt><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">007revad</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://opengraph.githubassets.com/bc8cbb6d211606d3ba81cdbe9a2a599f4e8ae8628ff1ef03fda2ae2f86b4e7a0/007revad/Synology_HDD_db" alt></div></a><figcaption>Latest script for Synology Drive Database</figcaption></figure><h3 id="what-the-script-does">What the script does</h3><ul><li>Gets the Synology NAS model and DSM version (so it knows which db files to edit).</li><li>Gets a list of the HDD, SSD, SAS and NVMe drives installed in your Synology NAS.</li><li>Gets each drive&apos;s model number and firmware version.</li><li>Backs up the database files if there is no backup already.</li><li>Checks if each drive is already in the Synology&apos;s compatible-drive database.</li><li>Adds any missing drives to the Synology&apos;s compatible-drive database.</li><li>Prevents DSM auto updating the drive database.</li><li>Optionally disable DSM&apos;s &quot;support_disk_compatibility&quot;.</li><li>Optionally disable DSM&apos;s &quot;support_memory_compatibility&quot; to prevent <a href="https://github.com/007revad/Synology_HDD_db/blob/main/images/ram_warning.png">non-Synology memory notifications</a>.</li><li>Optionally edits max supported memory to match the amount of memory installed, if installed memory is greater than the current max memory setting.</li><li>DSM only uses the max memory setting when calculating the reserved RAM area size for SSD caches.</li><li>Optionally disables Western Digital Device Analytics (aka WDDA) to prevent DSM showing a <a href="https://arstechnica.com/gadgets/2023/06/clearly-predatory-western-digital-sparks-panic-anger-for-age-shaming-hdds" rel="nofollow">warning for WD drives that are 3 years old</a>.</li><li>DSM 7.2.1 already has WDDA disabled.</li><li>Enables M2D20, M2D18, M2D17 and E10M20-T1 if present on Synology NAS that don&apos;t officially support them.</li><li>Checks that M.2 volume support is enabled (on models that have M.2 slots or PCIe slots).</li><li>Enables creating M.2 storage pools and volumes from within Storage Manager <strong>(newer models only?)</strong>.</li><li>Including M.2 drives in PCIe adaptor cards like M2D20, M2D18, M2D17 and E10M20-T1 for DSM 7.2.1 and above <strong>(need to run script after each boot)</strong>.</li><li>Makes DSM recheck disk compatibility so rebooting is not needed if you don&apos;t have M.2 drives (DSM 7 only).</li><li><strong>If you have M.2 drives you may need to reboot.</strong></li><li>Reminds you that you may need to reboot the Synology after running the script.</li><li>Checks if there is a newer version of this script and offers to download it for you.</li><li>The new version available messages time out so they don&apos;t prevent the script running if it is scheduled to run unattended.</li></ul><h1 id="create-partition">Create Partition</h1><p>Login as <strong><strong>root</strong></strong> <code>sudo -s</code> &#xA0;with SSH and type :</p><pre><code class="language-bash">ls /dev/nvme*</code></pre><blockquote>In this case <code>/dev/nvme0n1</code></blockquote><p>You will see the <code>/dev/nvme0n1</code> or <code>/dev/nvme1n1</code> depend on which slot you install the SSD.</p><blockquote>If your SSD at slot 2, use <code>/dev/nvme1n1</code> instead</blockquote><figure class="kg-card kg-code-card"><pre><code class="language-bash">fdisk -l /dev/nvme0n1
</code></pre><figcaption>You wil see the disk information.</figcaption></figure><pre><code>Disk /dev/nvme0n1: 931.5 GiB, 1000204886016 bytes, 1953525168 sectors
Disk model: Samsung SSD 980 PRO 1TB
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes</code></pre><p>With this command we check</p><pre><code class="language-bash">synopartition --check /dev/sata1</code></pre><figure class="kg-card kg-code-card"><pre><code>/dev/sata1: partition layout is version 8, list index is 14.</code></pre><figcaption>Result: Remember: 14</figcaption></figure><p>We use <code>14</code> for the next command.</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">synopartition --part /dev/nvme0n1 14</code></pre><figcaption>Command</figcaption></figure><blockquote><code>--part [--force] disk_path index_of_list(1~20) [logical_size_GB ... 0]<br>Partition the disk with specific layout.</code></blockquote><figure class="kg-card kg-code-card"><pre><code>        Device   Sectors (Version6: SupportRaid)
 /dev/nvme0n11   4980087 (2431 MB)
 /dev/nvme0n12   4192965 (2047 MB)
Reserved size:    257040 ( 125 MB)
Primary data partition will be created.

WARNING: This action will erase all data on &apos;/dev/nvme0n1&apos; and repart it, are you sure to continue? [y/N]</code></pre><figcaption>Result</figcaption></figure><p>and answer <code>y</code> if you confirm</p><pre><code>WARNING! You have only one disk.
Cleaning all partitions...
Creating sys partitions...
Creating primary data partition...
Please remember to mdadm and mkfs new partitions.</code></pre><p>it will create the partition that follow DSM required layout.</p><p>Type</p><pre><code class="language-bash"> fdisk -l /dev/nvme0n1
</code></pre><p>You will see the partition layout is created</p><figure class="kg-card kg-code-card"><pre><code>Disk /dev/nvme0n1: 931.5 GiB, 1000204886016 bytes, 1953525168 sectors
Disk model: Samsung SSD 980 PRO 1TB
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x75e5d2a8

Device         Boot   Start        End    Sectors  Size Id Type
/dev/nvme0n1p1         2048    4982527    4980480  2.4G fd Linux raid autodetect
/dev/nvme0n1p2      4982528    9176831    4194304    2G fd Linux raid autodetect
/dev/nvme0n1p3      9437184 1953520064 1944082881  927G fd Linux raid autodetect</code></pre><figcaption>Remember <code>/dev/nvme0n1p3</code></figcaption></figure><h1 id="create-filesystem">Create FileSystem</h1><h2 id="format-partition">Format Partition</h2><p>Formatting as Ext4 or BTRFS</p><h3 id="option-brtfs">Option: BRTFS</h3><figure class="kg-card kg-code-card"><pre><code class="language-bash">mkfs.btrfs -f /dev/nvme0n1p3</code></pre><figcaption>Format NVMe Partition 3 as BTRFS</figcaption></figure><h3 id="option-ext4">Option: Ext4</h3><figure class="kg-card kg-code-card"><pre><code class="language-bash">mkfs.ext4 -F /dev/nvme0n1p3</code></pre><figcaption>Format NVMe Partition 3 as Ext4</figcaption></figure><figure class="kg-card kg-code-card"><pre><code>mke2fs 1.44.1 (24-Mar-2018)
Creating filesystem with 243010096 4k blocks and 60760064 inodes
Filesystem UUID: 12aee3b8-c62b-46c4-88f7-a412d8010017
Superblock backups stored on blocks:
	32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632, 2654208,
	4096000, 7962624, 11239424, 20480000, 23887872, 71663616, 78675968,
	102400000, 214990848

Allocating group tables: done
Writing inode tables: done
Creating journal (262144 blocks): done
Writing superblocks and filesystem accounting information: done</code></pre><figcaption>Result</figcaption></figure><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---BTRFS---LUN-SAN.png" width="1280" height="458" loading="lazy" alt srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Synology-Storage-Manager---BTRFS---LUN-SAN.png 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Synology-Storage-Manager---BTRFS---LUN-SAN.png 1000w, https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---BTRFS---LUN-SAN.png 1280w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---BTRFS---SAN-Manager.png" width="1280" height="766" loading="lazy" alt srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Synology-Storage-Manager---BTRFS---SAN-Manager.png 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Synology-Storage-Manager---BTRFS---SAN-Manager.png 1000w, https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---BTRFS---SAN-Manager.png 1280w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>BTRFS - Virtual Machine</figcaption></figure><p></p><h1 id="attach-new-partition-to-new-raid-array">Attach new partition to new RAID array</h1><p>We need to see how many Multiple Devices (md) are, and attach the NVMe partition to new one.</p><p>Type:</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">cat /proc/mdstat
</code></pre><figcaption>To see your current RAID setup, and add next index number after <code>md</code>. For example <code>md3</code></figcaption></figure><figure class="kg-card kg-code-card"><pre><code>Personalities : [raid1]
md2 : active raid1 sata1p5[0]
      15621042624 blocks super 1.2 [1/1] [U]

md1 : active raid1 sata1p2[0]
      2097088 blocks [4/1] [U___]

md0 : active raid1 sata1p1[0]
      2490176 blocks [4/1] [U___]

unused devices: &lt;none&gt;</code></pre><figcaption>The last index is <code>md2</code>, the next index would be <code>md3</code>. Remember for later: <code>md3</code></figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-bash">mdadm --create /dev/md3 --level=1 --raid-devices=1 --force /dev/nvme0n1p3</code></pre><figcaption>Attach <code>/dev/nvme0n1p3</code> to new RAID array <code>md3</code></figcaption></figure><!--kg-card-begin: html--><p>Continue creating array? Answer: <code>y</code> and press <kbd>Enter</kbd><!--kg-card-end: html--><pre><code>mdadm: Note: this array has metadata at the start and
    may not be suitable as a boot device.  If you plan to
    store &apos;/boot&apos; on this device please ensure that
    your boot-loader understands md/v1.x metadata, or use
    --metadata=0.90
Continue creating array? y
mdadm: Defaulting to version 1.2 metadata
mdadm: array /dev/md3 started.</code></pre><blockquote>Continue creating array? Press <code>y</code> and ENTER</blockquote></p><h2 id="reboot">Reboot</h2><figure class="kg-card kg-code-card"><pre><code class="language-bash">reboot</code></pre><figcaption>After reboot check the DSM</figcaption></figure><h1 id="online-assemble-on-dsm">Online Assemble on DSM</h1><p>And after the machine bootup, you will see in the Storage Manager, Storage <strong>Available Pool 1</strong></p><ul><li>Go to: Available Pool 1</li><li>Look for the three-dots (...) More-icon and hit: Online Assemble</li><li>Hit: Apply</li></ul><h2 id="view-m2-drive">View (M.2 Drive)</h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---M2-Drive---Online-Assemble---1---Available-Pool-1.png" class="kg-image" alt loading="lazy" width="1280" height="735" srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Synology-Storage-Manager---M2-Drive---Online-Assemble---1---Available-Pool-1.png 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Synology-Storage-Manager---M2-Drive---Online-Assemble---1---Available-Pool-1.png 1000w, https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---M2-Drive---Online-Assemble---1---Available-Pool-1.png 1280w" sizes="(min-width: 720px) 720px"><figcaption>Available Pool 1</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---M2-Drive---Available-Pool-1---Context-Menu-Online-Assemble.png" class="kg-image" alt loading="lazy" width="1280" height="127" srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Synology-Storage-Manager---M2-Drive---Available-Pool-1---Context-Menu-Online-Assemble.png 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Synology-Storage-Manager---M2-Drive---Available-Pool-1---Context-Menu-Online-Assemble.png 1000w, https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---M2-Drive---Available-Pool-1---Context-Menu-Online-Assemble.png 1280w" sizes="(min-width: 720px) 720px"><figcaption>Hit More-icon Three-dots(...); Context Menu: Online Assemble</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---M2-Drive---Confirm-Settings.png" class="kg-image" alt loading="lazy" width="843" height="582" srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Synology-Storage-Manager---M2-Drive---Confirm-Settings.png 600w, https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---M2-Drive---Confirm-Settings.png 843w" sizes="(min-width: 720px) 720px"><figcaption>Confirm Settings: Apply</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---M2-Drive---Result-Storage-Pool-2-LUN-.png" class="kg-image" alt loading="lazy" width="1280" height="454" srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Synology-Storage-Manager---M2-Drive---Result-Storage-Pool-2-LUN-.png 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Synology-Storage-Manager---M2-Drive---Result-Storage-Pool-2-LUN-.png 1000w, https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---M2-Drive---Result-Storage-Pool-2-LUN-.png 1280w" sizes="(min-width: 720px) 720px"><figcaption>Overview Result: Online assembled</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---M2-Drive---Context-Menu-SAN-Manager.png" class="kg-image" alt loading="lazy" width="1280" height="169" srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Synology-Storage-Manager---M2-Drive---Context-Menu-SAN-Manager.png 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Synology-Storage-Manager---M2-Drive---Context-Menu-SAN-Manager.png 1000w, https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---M2-Drive---Context-Menu-SAN-Manager.png 1280w" sizes="(min-width: 720px) 720px"><figcaption>LUN Context Menu: SAN Manager</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---M2-Drive---SAN-Manager.png" class="kg-image" alt loading="lazy" width="1184" height="641" srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Synology-Storage-Manager---M2-Drive---SAN-Manager.png 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Synology-Storage-Manager---M2-Drive---SAN-Manager.png 1000w, https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---M2-Drive---SAN-Manager.png 1184w" sizes="(min-width: 720px) 720px"><figcaption>SAN Manager</figcaption></figure><h2 id="alternative-view-m2-cache">Alternative view (M.2 Cache)</h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager-Storage---Available-Pool-1.png" class="kg-image" alt loading="lazy" width="1279" height="738" srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Synology-Storage-Manager-Storage---Available-Pool-1.png 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Synology-Storage-Manager-Storage---Available-Pool-1.png 1000w, https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager-Storage---Available-Pool-1.png 1279w" sizes="(min-width: 720px) 720px"><figcaption>Storage Manager: Storage - Available Pool 1; After reboot new item is available</figcaption></figure><p>Then hit the More-icon (the three dots <code>...</code>) for Online Assemble</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---Context-Menu---Online-Assemble.png" class="kg-image" alt loading="lazy" width="1017" height="442" srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Synology-Storage-Manager---Context-Menu---Online-Assemble.png 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Synology-Storage-Manager---Context-Menu---Online-Assemble.png 1000w, https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---Context-Menu---Online-Assemble.png 1017w" sizes="(min-width: 720px) 720px"><figcaption>Context Menu: Online Assemble</figcaption></figure><p>Confirm settings, hit Apply</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---Confirm-Settings-Apply.png" class="kg-image" alt loading="lazy" width="837" height="579" srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Synology-Storage-Manager---Confirm-Settings-Apply.png 600w, https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---Confirm-Settings-Apply.png 837w" sizes="(min-width: 720px) 720px"><figcaption>Press: Apply then it will convert</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---Storage-Pool-2-Optimizing-file-system.png" class="kg-image" alt loading="lazy" width="1019" height="164" srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Synology-Storage-Manager---Storage-Pool-2-Optimizing-file-system.png 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Synology-Storage-Manager---Storage-Pool-2-Optimizing-file-system.png 1000w, https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---Storage-Pool-2-Optimizing-file-system.png 1019w" sizes="(min-width: 720px) 720px"><figcaption>Storage Pool 2: Volume 2 being Optimized</figcaption></figure><h1 id="result">Result</h1><p>As a result Storage Pool 2 is ready and healthy</p><h2 id="view-m2-drive-1">View (M.2 Drive)</h2><p>-- insert images --</p><h2 id="alternative-view-m2-cache-1">Alternative View (M.2 Cache)</h2><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager-Storage---Result---Overview.png" class="kg-image" alt loading="lazy" width="1279" height="708" srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Synology-Storage-Manager-Storage---Result---Overview.png 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Synology-Storage-Manager-Storage---Result---Overview.png 1000w, https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager-Storage---Result---Overview.png 1279w" sizes="(min-width: 720px) 720px"><figcaption>Storage Manager - Result - Overview - Build-In M.2 Slot have solid colour</figcaption></figure><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager-Storage---Result---Storage-Pool-2-Volume-2.png" class="kg-image" alt loading="lazy" width="1280" height="452" srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Synology-Storage-Manager-Storage---Result---Storage-Pool-2-Volume-2.png 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Synology-Storage-Manager-Storage---Result---Storage-Pool-2-Volume-2.png 1000w, https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager-Storage---Result---Storage-Pool-2-Volume-2.png 1280w" sizes="(min-width: 720px) 720px"><figcaption>Storage Manager - Result - Storage Pool 2 with Volume 2 is Healthy</figcaption></figure><h1 id="history">History</h1><p>My Storage Pool 2 stops working after reboot. Volume 2 didn&#x2019;t show. Very frustrating, until I found the script that updates device database. See below the screenshots what it look like when it fails.</p><h2 id="incompatible-nvme">Incompatible NVMe</h2><p>Everything seems fine, until you reboot your DSM (or have a power outage)</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---Incompatible-Device.jpg" class="kg-image" alt loading="lazy" width="1280" height="1702" srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Synology-Storage-Manager---Incompatible-Device.jpg 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Synology-Storage-Manager---Incompatible-Device.jpg 1000w, https://blog.sylo.space/content/images/2024/02/Synology-Storage-Manager---Incompatible-Device.jpg 1280w" sizes="(min-width: 720px) 720px"><figcaption>Critical device</figcaption></figure><h2 id="specs">Specs</h2><p>My NVMe specifications:</p><ul><li>Samsung PCIe 4.0 NVMe SSD 980 PRO 1 TB</li></ul><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://blog.sylo.space/content/images/2024/02/Samsung-SSD-980-Pro-img1.jpg" width="1200" height="329" loading="lazy" alt srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Samsung-SSD-980-Pro-img1.jpg 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Samsung-SSD-980-Pro-img1.jpg 1000w, https://blog.sylo.space/content/images/2024/02/Samsung-SSD-980-Pro-img1.jpg 1200w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://blog.sylo.space/content/images/2024/02/Samsung-SSD-980-Pro-img2.jpg" width="1200" height="640" loading="lazy" alt srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Samsung-SSD-980-Pro-img2.jpg 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Samsung-SSD-980-Pro-img2.jpg 1000w, https://blog.sylo.space/content/images/2024/02/Samsung-SSD-980-Pro-img2.jpg 1200w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://blog.sylo.space/content/images/2024/02/Samsung-SSD-980-Pro-img3.jpg" width="1200" height="568" loading="lazy" alt srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Samsung-SSD-980-Pro-img3.jpg 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Samsung-SSD-980-Pro-img3.jpg 1000w, https://blog.sylo.space/content/images/2024/02/Samsung-SSD-980-Pro-img3.jpg 1200w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://blog.sylo.space/content/images/2024/02/Samsung-SSD-980-Pro-img4.jpg" width="729" height="1200" loading="lazy" alt srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Samsung-SSD-980-Pro-img4.jpg 600w, https://blog.sylo.space/content/images/2024/02/Samsung-SSD-980-Pro-img4.jpg 729w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://blog.sylo.space/content/images/2024/02/Samsung-SSD-980-Pro-img5.jpg" width="1200" height="838" loading="lazy" alt srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Samsung-SSD-980-Pro-img5.jpg 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Samsung-SSD-980-Pro-img5.jpg 1000w, https://blog.sylo.space/content/images/2024/02/Samsung-SSD-980-Pro-img5.jpg 1200w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://blog.sylo.space/content/images/2024/02/Samsung-SSD-980-Pro-img6.jpg" width="1200" height="584" loading="lazy" alt srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Samsung-SSD-980-Pro-img6.jpg 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Samsung-SSD-980-Pro-img6.jpg 1000w, https://blog.sylo.space/content/images/2024/02/Samsung-SSD-980-Pro-img6.jpg 1200w" sizes="(min-width: 720px) 720px"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://blog.sylo.space/content/images/2024/02/Samsung-SSD-980-Pro-img7.jpg" width="771" height="1200" loading="lazy" alt srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Samsung-SSD-980-Pro-img7.jpg 600w, https://blog.sylo.space/content/images/2024/02/Samsung-SSD-980-Pro-img7.jpg 771w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://blog.sylo.space/content/images/2024/02/Samsung-SSD-980-Pro-img8.jpg" width="730" height="1200" loading="lazy" alt srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Samsung-SSD-980-Pro-img8.jpg 600w, https://blog.sylo.space/content/images/2024/02/Samsung-SSD-980-Pro-img8.jpg 730w" sizes="(min-width: 720px) 720px"></div><div class="kg-gallery-image"><img src="https://blog.sylo.space/content/images/2024/02/Samsung-SSD-980-Pro-img9.jpg" width="1200" height="330" loading="lazy" alt srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Samsung-SSD-980-Pro-img9.jpg 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Samsung-SSD-980-Pro-img9.jpg 1000w, https://blog.sylo.space/content/images/2024/02/Samsung-SSD-980-Pro-img9.jpg 1200w" sizes="(min-width: 720px) 720px"></div></div></div><figcaption>Samsung PCIe 4.0 NVMe SSD 980 PRO 1 TB</figcaption></figure><h2 id="revive-your-critical-device">Revive your critical device</h2><p>You can go back and remove the Storage Pool (Critical Device).</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2024/02/Storage-Manager---Remove-Storage-Pool-2---Critical-Device.jpg" class="kg-image" alt loading="lazy" width="1280" height="2376" srcset="https://blog.sylo.space/content/images/size/w600/2024/02/Storage-Manager---Remove-Storage-Pool-2---Critical-Device.jpg 600w, https://blog.sylo.space/content/images/size/w1000/2024/02/Storage-Manager---Remove-Storage-Pool-2---Critical-Device.jpg 1000w, https://blog.sylo.space/content/images/2024/02/Storage-Manager---Remove-Storage-Pool-2---Critical-Device.jpg 1280w" sizes="(min-width: 720px) 720px"><figcaption>1. (...) Context Menu; 2. Remove; 3. Remove, Delete, type password and Submit.</figcaption></figure><blockquote>Assuming DSM creates a record for the Storage Pool, that&#x2019;s still active and for some reason unable to find a device.<br>The device <code>/dev/md3</code> seems to be gone after Online Assemble and the reboot.</blockquote><p>Go back to your terminal and format your NVMe. Remember your last address you messed around: <code>/dev/nvme0n1</code></p><p>Login as <strong><strong><strong><strong>root</strong></strong></strong></strong><code>sudo -s</code> &#xA0;with SSH and type:</p><figure class="kg-card kg-code-card"><pre><code class="language-bash">synopartition --part /dev/nvme0n1 14</code></pre><figcaption>Command</figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-bash">mkfs.btrfs -F /dev/nvme0n1p3</code></pre><figcaption>Format your NVMe again</figcaption></figure><figure class="kg-card kg-code-card"><pre><code class="language-bash">reboot</code></pre><figcaption>Reboot and check DSM</figcaption></figure><h2 id="fix">Fix</h2><p>Use the <a href="#use-script-to-add-your-drives-to-your-synologys-drive-compatibility-database-v1216">script</a></p><h1 id="trouble-shoot">Trouble Shoot</h1><h2 id="i-removed-storage-pool-2-by-accident">I removed Storage Pool 2 by accident</h2><p>You&#x2019;ve lost all your data. Start over again with re-partition your NVMe again.</p><h2 id="i-rebooted-my-synology">I rebooted my Synology</h2><p>When you followed my article and missed the &#x2018;Update my Synology HDD Database&#x2019; part. Use the <a href="#use-script-to-add-your-drives-to-your-synologys-drive-compatibility-database-v1216">script</a>.</p>]]></content:encoded></item><item><title><![CDATA[My first steps with Synology DS920+]]></title><description><![CDATA[<p>For a long time I&#x2019;ve been planning to buy a NAS and things I want to do with it, because I get sick and tired having the idea when they held my data hostage when some things might go awry. Also I want to store my private data</p>]]></description><link>https://blog.sylo.space/synology-ds920/</link><guid isPermaLink="false">620418b9140bec0001a0c9af</guid><category><![CDATA[Journal]]></category><category><![CDATA[Draft]]></category><dc:creator><![CDATA[Harianto van Insulinde]]></dc:creator><pubDate>Tue, 23 Nov 2021 08:50:00 GMT</pubDate><content:encoded><![CDATA[<p>For a long time I&#x2019;ve been planning to buy a NAS and things I want to do with it, because I get sick and tired having the idea when they held my data hostage when some things might go awry. Also I want to store my private data and easy to access while I host them myself.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2022/02/synology-ds920-plus.png" class="kg-image" alt loading="lazy" width="1336" height="800" srcset="https://blog.sylo.space/content/images/size/w600/2022/02/synology-ds920-plus.png 600w, https://blog.sylo.space/content/images/size/w1000/2022/02/synology-ds920-plus.png 1000w, https://blog.sylo.space/content/images/2022/02/synology-ds920-plus.png 1336w" sizes="(min-width: 720px) 720px"><figcaption>Synology DS920+</figcaption></figure><p>Some time ago (19 November 2021) I bought a NAS <a href="https://www.synology.com/en-uk/products/DS920+#specs">Synology DS920+</a>. It has 4 bays and has two cache slots, also and a slot to expand memory. Because 4 GB is not much, because I might run some Virtualisation and Docker. After for some research, there is a way to go over 4GB and found 16GB SODIMM. </p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2022/02/crucial-ddr4-sodimm.jpg" class="kg-image" alt loading="lazy" width="1500" height="645" srcset="https://blog.sylo.space/content/images/size/w600/2022/02/crucial-ddr4-sodimm.jpg 600w, https://blog.sylo.space/content/images/size/w1000/2022/02/crucial-ddr4-sodimm.jpg 1000w, https://blog.sylo.space/content/images/2022/02/crucial-ddr4-sodimm.jpg 1500w" sizes="(min-width: 720px) 720px"><figcaption>Crucial CT16G4SFRA266 DDR RAM, 16 GB</figcaption></figure><p>Later on, I bought first HDD Seagate IronWolf Pro 14TB from Amazon.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2022/02/seagate-ironwolf-pro-nas-16tb.jpg" class="kg-image" alt loading="lazy" width="1029" height="1500" srcset="https://blog.sylo.space/content/images/size/w600/2022/02/seagate-ironwolf-pro-nas-16tb.jpg 600w, https://blog.sylo.space/content/images/size/w1000/2022/02/seagate-ironwolf-pro-nas-16tb.jpg 1000w, https://blog.sylo.space/content/images/2022/02/seagate-ironwolf-pro-nas-16tb.jpg 1029w" sizes="(min-width: 720px) 720px"><figcaption>Seagate IronWolf Pro, 16 TB, Internal Hard Drive, NAS, CMR, 3.5&quot;, SATA, 6GB/s ,7200RPM, 256MB Cache, for NAS RAID, 3 Years of Rescue Services, FFP (ST16000NEZ00)</figcaption></figure><blockquote>Later I&#x2019;ve check it&#x2019;s better to get 2 HDD&apos;s instead of one (of the same size) for data retention</blockquote><p>So when I first received my HDD, it was time to check the NAS hardware and mount it.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://blog.sylo.space/content/images/2022/02/samsun-nvme-m2-980-pro.jpeg" class="kg-image" alt loading="lazy" width="1200" height="568" srcset="https://blog.sylo.space/content/images/size/w600/2022/02/samsun-nvme-m2-980-pro.jpeg 600w, https://blog.sylo.space/content/images/size/w1000/2022/02/samsun-nvme-m2-980-pro.jpeg 1000w, https://blog.sylo.space/content/images/2022/02/samsun-nvme-m2-980-pro.jpeg 1200w" sizes="(min-width: 720px) 720px"><figcaption>Samsung 980 PRO NVMe - Interne SSD M.2 PCIe - 1 TB</figcaption></figure><blockquote>DSM would only allow you setup NVMe as a DSM cache, but there is a way to use as storage.</blockquote><p>There aren&apos;t convincing data online that DSM cache really works and most of the Youtube only show bad test case for example data transfer. That&apos;s not how memory performance works.</p><blockquote>Until I found a way!</blockquote>]]></content:encoded></item><item><title><![CDATA[Upgrading this blog to Ghost 4]]></title><description><![CDATA[was very easy with my current docker setup.]]></description><link>https://blog.sylo.space/upgrading-this-blog-to-ghost-4/</link><guid isPermaLink="false">605bd1aff2c1c3000180557b</guid><category><![CDATA[Journal]]></category><dc:creator><![CDATA[Harianto van Insulinde]]></dc:creator><pubDate>Thu, 25 Mar 2021 00:02:03 GMT</pubDate><content:encoded><![CDATA[<p>I just change image <code>ghost:3-alpine</code> to <code>ghost:4-alpine</code> in my <em>docker-compose.yml</em>.</p><h1 id="upgrade-minor-version-and-patches">Upgrade Minor Version and Patches</h1><pre><code class="language-bash">docker pull ghost:4-alpine</code></pre>]]></content:encoded></item></channel></rss>