A personal journal.

Deploy Iceshrimp.NET di fly.io

Published on: 20/05/2024 • Updated on: 09/05/2025 • 7 min read

Kalau sebelumnya saya deploy GoToSocial, yang masih mendukung perangkat mini saya, kali ini saya akan menggunakan salah satu server yang memiliki banyak fitur, merupakan fork dari Firefish (ceritanya panjang, ini adalah fork dari fork, tapi intinya mirip Misskey) dan berasal dari NodeJS. Keluarlah rewrite menggunakan bahasa C# .NET, yang tentunya tidak mendukung armv6.

Pada laman dokumentasi asli dari iceshrimp.net (disini), sebetulnya sudah disediakan instalasi dalam bentuk docker. Namun, entah kenapa jika kamu menggunakan docker, kamu tidak bisa mendaftarkan akun. Ya, namanya juga alpha.

Akhirnya saya berinisiatif seperti ketika saya mencoba dendrite, saya akan membuat image docker saya sendiri. Beberapa masalah yang saya temukan adalah:

  • Copas menggunakan Dockerfile asli tidak bisa berjalan, karena ketika dotnet restore pasti ada protes kalau beberapa fsproj tidak ditemukan.
  • Menggunakan -o /app memang berhasil, namun ketika dijalankan, Iceshrimp masih beranggapan bahwa frontend berada di direktori lain, bukan di /app.
  • Tidak bisa menggunakan -publish karena, entah mengapa, ketika dipublikasikan ada protes bahwa file tidak ditemukan.

Sehingga saya mencoba seakurat dan sesederhana mungkin untuk membuat image ini.

{{< callout emoji=“💡” >}}

Hari ini, 22 Mei 2024, docker image resmi dari iceshrimp.net bekerja dengan baik, jadi kamu sekarang tidak perlu mengikuti langkah pembuatan Dockerfile yang saya jelaskan di bawah.

{{< /callout >}}

Dockerfile

Berikut adalah isi dari Dockerfile yang saya gunakan untuk dideploy ke fly.io:

# Builder
FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine as BUILDER
RUN apk add -U git
RUN dotnet workload install wasm-tools # boleh dihilangkan jika tidak ingin menggunakan AOT
# harusnya disini diberi args agar perintah dibawah akan diupdate ketika kita mau update buildnya.
RUN git clone https://iceshrimp.dev/iceshrimp/iceshrimp.net --depth=1
WORKDIR /iceshrimp.net/Iceshrimp.Backend
RUN dotnet build -c Release -p:EnableAOT=true # hapus EnableAOT jika tidak menggunakan wasm-tools

# Prod
FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine-composite as PROD
COPY --from=BUILDER /iceshrimp.net /iceshrimp.net
WORKDIR /iceshrimp.net/Iceshrimp.Backend/bin/Release/net8.0/linux-musl-x64
COPY ./configuration.overrides.ini . # Kamu harus menyiapkan config ini.

EXPOSE 3000
ENTRYPOINT ["./Iceshrimp.Backend", "--environment", "Production", "--migrate-and-start"]

Isi dari Dockerfile yang saya gunakan

Configuration.ini

Mengenai configuration.overrides.ini, kamu bisa copy paste file configuration.ini dan mengubah seperlunya, sebagai contoh seperti berikut.

[Instance]
;; karena didalam docker, maka kita harus listen ke semuanya
ListenHost = 0.0.0.0

WebDomain = iceshrimp.domain.kamu
;; jika kamu menginginkan split domain, kamu bisa mengisi ini
AccountDomain = domain.kamu

[Security]
;; Options: [Closed, Invite, Open]
;; perlu diganti untuk pertama kali agar kamu bisa mendaftarkan diri
Registrations = Closed

Beberapa konfigurasi yang perlu diganti

Tentunya database dan storage juga perlu kamu atur, namun saya tidak akan menjelaskannya. Ada permintaan dari rekan mengenai pengaturan storage.

Untuk konfigurasi pada fly.io, saya tidak akan menjelaskan, karena ketika kamu membuat proyek baru dan di dalam folder tersebut ada file Dockerfile, fly akan otomatis menggunakan file itu untuk dideploy.

Setup Storage dengan tebi.io

Pada dokumentasi iceshrimp.net terdapat tutorial untuk penyedia layanan Garage, Cloudflare R2, Blackblaze, Digital Ocean, namun tidak ada tebi, padahal tebi menurut saya sederhana dan it works.

Berikut adalah konfigurasi yang harus kamu masukkan kedalam configuration.overrides.ini jika menggunakan tebi:

[Storage:ObjectStorage]
Endpoint = s3.tebi.io
Region = us-east-1
KeyId = <keyID>
SecretKey = <applicationKey>
Bucket = <bucket name>
;;Prefix =
AccessUrl = https://s3.tebi.io/<nama bucket>

configuration.overrides.ini untuk storage

  • Sedikit penjelasan tentang Region, tebi tidak mengenal region (ada, namun tidak diperlukan ketika kita “mengakses” bucket nya karena kita bisa mengatur itu pada dasbor tebi) namun karena iceshrimp ini minta, ya kita kasih. Kamu bisa menggunakan region apa saja seperti yang ada pada dokumentasi aws.
  • Sedangkan untuk AccessUrl bucket pada tebi pada dasarnya adalah public, dan itu adalah nama bucket yang sudah kamu buat, misalnya saya pernah membuat bucket bernama gts, maka bisa dibuka dengan menuju ke alamat https://s3.tebi.io/gts/ atau https://gts.s3.tebi.io/.
  • Silahkan isi Prefix jika kamu ingin file kamu berada didalam folder di bucket.

Sifat bucket pada tebi adalah unik, yang artinya kamu tidak bisa membuat bucket dengan nama gts. Semua orang bisa mengakses bucket tersebut, namun secara default tidak ada peraturan Access Policy pada tebi, sehingga kamu tidak akan bisa iseng membuka bucket orang lain tanpa ijin meski orangnya tidak sadar, hal ini juga terjadi pada instalasi iceshrimp kita, tidak bisa akses kecuali kita berikan akses.

Mari kita menuju dasbor tebi, kemudian Edit Bucket dan masuk ke tab Bucket Policy, enable dan masukkan kode berikut (kode ini saya ambil dari dokumentasi tebi):

{
"Version": "2012-10-17",
"Id": "S3PolicyAllow-IP",
"Statement": [{
    "Sid": "IP-Allow",
    "Effect": "Allow",
    "Action": ["s3:ListObjects","s3:GetObject", "s3:HeadObject", "s3:DeleteObject", "s3:PutObject"],
    "Resource": ["bucket/folder", "bucket/folder/*"],
    "Condition": {
        "IpAddress": {"aws:SourceIp": ["<ip machine fly io kamu>", "<ip public kamu jika mau>"]},
    }
  }]
}

Access Policy pada tebi.io

  • IP machine dapat kamu ambil ketika ada pada laman overview, yang perlu diperhatikan adalah IPv4 pada fly.io sekarang bersifat shared, jadi bisa saja ada orang iseng 1 IP pada fly.io bisa menguntit.
  • Resource bisa diisi spesifik “bucket dan folder mana yang boleh diakses”, alias kombinasi prefix pada konfigurasi sebelumnya. Kamu bisa isi dengan format <nama bucket>/<nama folder> . Atau semuanya saja.

{{< callout emoji=“💡” >}}

Sebetulnya saya masih kurang begitu paham dengan policy yang diperlukan, namun jika berkaca dari dokumentasi iceshrimp.net mereka hanya menuliskan put, delete, dan get saja.

{{< /callout >}}

{{< callout emoji=“⚠️” >}}

Jika anda seperti saya yang menggunakan Iceshrimp.NET ini sejak awal, terdapat bug pada Iceshrimp yang membuat media tidak terhapus meskipun sudah dihapus dari database. Kamu bisa menjalankan iceshrimp.backend --cleanup-storage untuk membersihkan media yang gagal terhapus karena bug ini.

{{< /callout >}}

Split Domain

Split domain adalah cara dimana kamu bisa host iceshrimp mu di iceshrimp.domain.kamu dan menggunakan username seperti [email protected], lebih pendek dan keren bukan? caranya cukup mudah, bagi yang punya perangkat sendiri. Penjelasan bagus ada di laman milik GoToSocial disini.

Sama seperti ketika saya mencoba dendrite, saya pun memanfaatkan cloudflare worker untuk melakukan redirect ini. Apakah bekerja sesuai harapan? ya! namun ini software alpha.. dimana beberapa kali /.well-known/webfinger, /.well-known/host-meta, /.well-known/nodeinfo tidak merespon seperti yang seharusnya. Berikut adalah skrip yang saya gunakan, saya tidak bilang skrip ini sangat bagus ya:

addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
  // Dapatkan URL permintaan
  const url = new URL(request.url)

  // Periksa apakah permintaan menuju ke .well-known/webfinger
  if (url.pathname === '/.well-known/webfinger') {
    // Buat objek baru untuk menyimpan parameter
    const params = new URLSearchParams()

    // Salin semua parameter dari URL permintaan ke objek params
    for (const [key, value] of url.searchParams) {
      params.set(key, value)
    }

    // Buat URL tujuan redirect
    const redirectUrl = `iceshrimp.domain.kamu/.well-known/webfinger?${params.toString()}`

    // Kembalikan respons redirect
    return Response.redirect(redirectUrl, 301)
  }
  // Periksa apakah permintaan menuju ke .well-known/host-meta
  else if (url.pathname === '/.well-known/host-meta') {
    // Buat URL tujuan redirect
    const redirectUrl = 'https://iceshrimp.domain.kamu/.well-known/host-meta'

    // Kembalikan respons redirect
    return Response.redirect(redirectUrl, 301)
  }
  // Periksa apakah permintaan menuju ke .well-known/nodeinfo
  else if (url.pathname === '/.well-known/nodeinfo') {
    // Buat URL tujuan redirect
    const redirectUrl = 'https://iceshrimp.domain.kamu/.well-known/nodeinfo'

    // Kembalikan respons redirect
    return Response.redirect(redirectUrl, 301)
  }

  // Jika bukan permintaan ke .well-known/webfinger, .well-known/host-meta, atau .well-known/nodeinfo, lanjut
  // return fetch(request)

  // atau kembalikan respons ke eror (domain saya ini tidak terpakai)
  return new Response('Hello World, nothing here', { status: 404 })
}

worker.js

Selanjutnya kamu bisa mengatur routing ke workers ini, sebagai info terdapat perubahan pada halaman routing cloudflare workers (jika dibandingkan ketika saya membuat routing pada dendrite) dimana waktu itu ada pengaturan route dan zone.

Untuk mengatur route, kamu bisa menuju halaman dari worker yang sudah kamu buat (bukan halaman edit) kemudian buka tab Settings > Triggers. Tampilannya kurang lebih seperti ini:

![Tampilan baru routing pada cloudflare workers

](https://res.cloudinary.com/dgvv7srnp/image/upload/q_auto/blog/Screenshot-2024-05-27-at-08-45-27-W3d03sss-Cloudflare.png)

Pada setup saya, saya menggunakan 1 domain saya (yang tidak terpakai) sehingga saya menggunakan Custom Domains dan bukan Routes, sehingga semua request yang akan menuju domain ktem.eu.org akan di proses oleh 1 workers ini. Untuk penjelasannya kamu bisa membaca dokumentasi dari cloudflare workers mana pengaturan yang tepat untukmu disini.

Setelah status certificatenya berubah menjadi valid, kamu bisa melakukan cek domain kamu apakah sudah berhasil redirect dengan benar, misalnya saya membuka https://ktem.eu.org/.well-known/nodeinfo seharusnya akan diredirect ke https://ice.ktem.eu.org/.well-known/nodeinfo. Lakukan juga pada webfinger dan host-meta.

Khusus untuk webfinger, kamu setidaknya harus mencoba untuk membuka laman dengan format seperti ini: domain.kamu/.well-known/webfinger?resource=acct:[email protected]. Pastikan outputnya tidak 400 BadRequest karena parameternya tidak ikut dibawa ketika redirect.

Viola split domain. Setidaknya kalau saya lihat dari server teman saya disini.

Machine di Fly.io restart?

Mungkin kamu menginginkan untuk menambahkan 1 baris berisikan swap_size_mb = 512 kedalam fly.toml jika kamu menggunakan shared machine dengan ram 256 MB. Atau tentu saja, kamu bisa scaling memori pada fly.io dengan perintah fly scale memory.

Ada toot yang tidak masuk?

Jika toot yang dimaksud adalah toot dengan media seperti gambar / video, kemungkinan VM milik fly kita yang terbatas ini mengalami timeout. Solusinya tentu kamu bisa melakukan retry job pada jobs yang mengalami timeout.

Namun jika toot yang dimaksud dari server lain, ada beberapa penyebab:

- Threads, lemmy, bridgy : tidak menggunakan actor, tidak sesuai standar AP jadi dev Iceshrimp.NET memilih menunda untuk memperbaiki ini.

- server AP : kemungkinan kesalahan dalam mengatur webfinger.

Mendaftarkan akun

Untuk mendaftarkan akun, kamu perlu mengubah 1 baris konfigurasi yang sebelumnya sudah saya tuliskan. Agar lebih mudah, kamu bisa membuka iceshrimp.domain.anda/swagger dan mencari bagian auth register. Kamu bisa menggunakan alat tersebut.

{{< callout emoji=“💡” >}}

Jangan lupa, setelah berhasil mendaftar, matikan konfigurasi registrasi di atas.

{{< /callout >}}

Seingat saya, sampai tulisan ini ditayangkan, Iceshrimp.NET belum memiliki antarmuka, jadi kamu bisa menggunakan antarmuka lain yang ada di internet yang mendukung Mastodon atau dengan menggunakan aplikasi di smartphone Anda. Ya, program ini masih dalam tahap alpha, sekarang sudah BETA!

Jika nanti saya teringat sesuatu, saya akan memperbarui tulisan ini.

small note: entah kenapa saya selalu menuliskan entah kenapa, alasannya tentu saya tidak tahu kenapa. duh.