Mert Özen Her Satırda Daha İleri
DevOps Linux ve Shell (geliştirici için) — #02

Linux File System Hiyerarşisi: /etc, /var, /opt, /srv, /home, /proc ve "Her Şey Bir Dosya"

Mert Özen 14 Kas 2026 13 dk 3 görüntülenme
Linux File System Hiyerarşisi: /etc, /var, /opt, /srv, /home, /proc ve "Her Şey Bir Dosya"

Linux'ta program /usr ya da /opt'ta, configuration /etc'de, değişen veri /var'da durur; Bookstore için bu /opt/bookstore/releases, /etc/bookstore, /var/log/bookstore ve /var/lib/bookstore demek. find, cp -a ve rm -r'yi, hard link ile symlink farkını, current symlink'ini çevirirken ln -sf'nin sessizce yanlış yere link açtığını, /proc/<pid> ile çalışan bir .NET process'ine dışarıdan bakmayı ve appsettings.production.json'un Linux'ta neden okunmadığını gerçek çıktılarla gösteriyoruz.

Sürüm notu: Bu yazıdaki komutlar Ubuntu Server 24.04.5 LTS (kernel 6.8, coreutils 9.4, bash 5.2) çalıştıran bir test VM'inde, ASP.NET Core 10.0.15 runtime'ıyla denenmiştir.

Önce Tahmin Et

Sunucuda sitedeki serilerin ortak örnek uygulaması Bookstore'un (bir online kitapçı) API'si için her release'i ayrı bir klasöre koyuyorsun ve /opt/bookstore/current adında bir symlink o an çalışan release'i gösteriyor. Yeni release'i kopyaladın ve link'i çevirmek için şunu yazdın:

$ ls -l /opt/bookstore
total 4
lrwxrwxrwx 1 root root   36 Nov 14 14:38 current -> /opt/bookstore/releases/2026-11-14-1
drwxr-xr-x 4 root root 4096 Nov 14 14:31 releases
$ sudo ln -sf /opt/bookstore/releases/2026-11-14-2 /opt/bookstore/current
$ readlink /opt/bookstore/current

Son komut ne yazar? -f "varsa üzerine yaz" demek; link'in yeni release'i göstermesini bekliyorsun. Komut hata vermedi. Cevap yazının ortasında, hard link ve symlink farkını konuştuğumuz yerde.

İlk yazıda sunucuya key ile girdik ve Bookstore.Api'yi ev klasörümüze kopyaladık. Ev klasörü deneme için iyi, ama bir uygulamanın sunucudaki yeri orası değil. Bu yazıda Linux'un dizin yapısının mantığını, Bookstore'un dosyalarının nereye konacağını, gezinme ve dosya komutlarını, link'leri ve diskte olmayan dosyaları anlatıyoruz.

FHS: Her Şeyin Bir Yeri Var

Linux'ta tek bir ağaç var ve kökü /. Windows'taki C:\, D:\ gibi sürücü harfleri yok; ikinci bir disk de bu ağacın içinde bir klasöre bağlanır (mount edilir). Hangi klasörün ne işe yaradığını FHS (Filesystem Hierarchy Standard; Linux dizin yapısını tarif eden standart) belirler. Ana fikir şu: değişmeyen programlar, makineye özel configuration ve çalışırken değişen veri ayrı yerlerde durur. Böylece programı silip yeniden kurarken configuration'a, yedek alırken programlara dokunmazsın.

02-fhs-bookstore-tr.png

$ ls /
bin                dev   lib                lost+found  opt   run                 snap  tmp
bin.usr-is-merged  etc   lib.usr-is-merged  media       proc  sbin                srv   usr
boot               home  lib64              mnt         root  sbin.usr-is-merged  sys   var
$ ls -l / | grep -- "->"
lrwxrwxrwx   1 root root     7 Apr 22  2024 bin -> usr/bin
lrwxrwxrwx   1 root root     7 Apr 22  2024 lib -> usr/lib
lrwxrwxrwx   1 root root     9 Apr 22  2024 lib64 -> usr/lib64
lrwxrwxrwx   1 root root     8 Apr 22  2024 sbin -> usr/sbin

Eski kaynaklarda /bin ile /usr/bin'in farkını okursan şaşırma: Ubuntu'da artık /bin, /lib ve /sbin birer symlink, programların hepsi /usr altında. Bir geliştirici için önemli olan klasörler şunlar:

  • /etc: makineye özel configuration. Nginx'in nginx.conf'u, SSH (uzak bağlantı protokolü) sunucusunun sshd_config'i, Bookstore için /etc/bookstore/. Burada çalıştırılabilir program olmaz.
  • /usr: paket yöneticisinin kurduğu programlar ve kütüphaneler. Ubuntu deposundan kurduğumuz .NET runtime /usr/lib/dotnet'te. Elle buraya dosya koyma; kendi kurduğun araçlar için /usr/local var.
  • /opt: paket sisteminin dışından gelen, kendi klasöründe yaşayan uygulamalar. Bookstore.Api tam olarak bu: /opt/bookstore/.
  • /var: çalışırken değişen veri. /var/log log'lar, /var/lib uygulamaların kalıcı durumu (veritabanı dosyaları, Data Protection key'leri), /var/cache silinse de yeniden üretilebilecek veri.
  • /srv: bu makinenin dışarıya sunduğu veri (statik site dosyaları, dışarıya açılan dosya paylaşımları). İç yapısını yönetici belirler.
  • /home: kullanıcıların kendi dosyaları. /root ise root kullanıcısının ev klasörü.
  • /tmp: geçici dosyalar. Ubuntu 24.04'te systemd-tmpfiles /tmp'yi her açılışta temizliyor ve 30 günden eski dosyaları siliyor; kalıcı bir şeyi buraya koyma.
  • /proc, /sys, /dev, /run: diskte olmayan, kernel'in ve systemd'nin anlık ürettiği dosyalar. Aşağıda bakacağız.

Bookstore İçin Bir Yerleşim

Bu seride Bookstore'u şöyle yerleştiriyoruz: program /opt/bookstore/releases/<release> altında, çalışan release'i /opt/bookstore/current symlink'i gösteriyor; configuration ve secret'lar /etc/bookstore/'da, log dosyaları /var/log/bookstore/'da, uygulamanın saklaması gereken durum /var/lib/bookstore/'da. Web Server'lar ve Hosting serisinin systemd yazısında release'leri /var/www/bookstore altına koymuştuk; o da yaygın bir tercih. FHS'ye daha yakın olduğu için burada /opt kullanıyoruz; hangisini seçtiğin, seçtiğin yerde tutarlı kalmandan daha az önemli.

$ sudo mkdir -p /opt/bookstore/releases /etc/bookstore /var/log/bookstore /var/lib/bookstore
$ sudo cp -a ~/bookstore-api /opt/bookstore/releases/2026-11-14-1
$ sudo ln -sfn /opt/bookstore/releases/2026-11-14-1 /opt/bookstore/current
$ tree -L 2 /opt/bookstore
/opt/bookstore
├── current -> /opt/bookstore/releases/2026-11-14-1
└── releases
    └── 2026-11-14-1

4 directories, 0 files

mkdir -p eksik ara klasörleri de oluşturur ve klasör zaten varsa hata vermez. cp -a (archive) alt klasörleri, izinleri, sahipliği, zamanları ve link'leri korur; düz cp -r dosyaların sahibini kopyalayan kullanıcıya çevirir. Dosyalar şu an root'a ait klasörlerde ama sahipleri hâlâ ada; uygulamanın hangi kullanıcıyla çalışacağı ve kimin neye yazabileceği bir sonraki yazının konusu. tree Ubuntu'da varsayılan gelmiyor, sudo apt install tree ile kuruluyor; -L 2 iki seviyeden aşağı inmediği için release'in içindeki dosyalar listede yok.

Gezinmek: Absolute ve Relative Path

/ ile başlayan her path absolute'tur ve nerede olursan ol aynı yeri gösterir. Diğerleri relative'dir ve bulunduğun klasöre göre çözülür: . bulunduğun klasör, .. bir üstü, ~ ev klasörün. Symlink'lerin içinden geçerken bash'in "mantıksal" yolu hatırladığını bil:

$ cd /opt/bookstore/current
$ pwd
/opt/bookstore/current
$ pwd -P
/opt/bookstore/releases/2026-11-14-1
$ cd ../..; pwd
/opt
$ cd -
/opt/bookstore/current
$ cd ~; pwd
/home/ada

pwd senin geldiğin yolu, pwd -P gerçek fiziksel yolu gösterir. cd ../.. bash'te mantıksal yola göre çalıştığı için /opt'a çıktı; fiziksel yoldan gitseydik /opt/bookstore'da olurduk. cd - bir önceki klasöre döner. Script'lerde ve configuration dosyalarında relative path kullanmak, komutun hangi klasörden çalıştırıldığına bağımlı olmak demek; systemd unit'lerinde ve cron'da absolute path yazmamızın sebebi bu (beşinci yazıda tekrar göreceğiz).

Dosya İşlemleri ve find

cp, mv, rm ve mkdir Windows'taki karşılıklarına benziyor, iki farkla. Birincisi, rm geri dönüşüm kutusuna göndermez: rm -r ile sildiğin klasör gider ve geri getirmenin standart bir yolu yok. sudo rm -rf $DIR/ gibi bir satırda değişken boşsa komut /'a döner; değişkenli silme komutlarında önce ls ile neyi sileceğine bak. İkincisi, mv aynı file system içinde dosyayı kopyalamaz, yalnızca adını değiştirir; bu yüzden büyük bir klasörü bile anında taşır.

Bir dosyayı aramak için find: ilk argüman nereden başlanacağı, gerisi koşullar.

$ find /opt/bookstore/releases -name "*.dll" -size +5k
/opt/bookstore/releases/2026-11-14-1/Bookstore.Api.dll
/opt/bookstore/releases/2026-11-14-2/Bookstore.Api.dll
$ sudo find /etc -name "*.conf" -newer /etc/hostname -type f
/etc/ssh/sshd_config.d/50-cloud-init.conf
$ sudo find /var/log -type f -mmin -60 | head -5
/var/log/dpkg.log
/var/log/wtmp
/var/log/kern.log
/var/log/alternatives.log
/var/log/apport.log
$ sudo find /var -xdev -type f -size +20M -exec ls -lh {} +
-rw-r--r-- 1 root root 25M Nov 10 22:07 /var/cache/apt/archives/dotnet-runtime-10.0_10.0.15-0ubuntu1~24.04.1_amd64.deb
-rw-r--r-- 1 root root 57M Nov 14 14:30 /var/cache/apt/pkgcache.bin
-rw-r--r-- 1 root root 57M Nov 14 14:10 /var/cache/apt/srcpkgcache.bin
-rw-r--r-- 1 root root 70M Apr 25  2024 /var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_noble_universe_binary-amd64_Packages
-rw-r--r-- 1 root root 31M Apr 25  2024 /var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_noble_universe_i18n_Translation-en

Sırasıyla: isim ve boyuta göre (-size +5k 5 KB'tan büyük), başka bir dosyadan daha yeni olanlar (-newer; "kurulumdan sonra hangi configuration değişti?" sorusuna iyi bir cevap), son 60 dakikada değişenler (-mmin -60; gün için -mtime) ve 20 MB'tan büyük dosyalar. -xdev aramayı başka bir file system'e geçirmez, -exec ... {} + bulunan dosyaları toplu olarak bir komuta verir. Son komut, "disk neden doluyor" sorusunun ilk adımı; onuncu yazıda du ile devam edeceğiz. Burada büyük dosyaların hepsi apt'nin cache'i ve paket listeleri.

Linux'ta bir dosyanın adı ile içeriği ayrı şeyler. İçerik ve izinler bir inode'da (dosyanın diskteki kaydı) durur; klasördeki ad yalnızca o inode'a bir referans. Hard link aynı inode'a ikinci bir ad, symlink ise içinde başka bir path yazan küçük bir dosya:

$ echo "Dune,14.99" > books.csv
$ ln books.csv books-hard.csv
$ ln -s books.csv books-sym.csv
$ ls -li
262656 -rw-rw-r-- 2 ada ada 11 Nov 14 14:31 books-hard.csv
262657 lrwxrwxrwx 1 ada ada  9 Nov 14 14:31 books-sym.csv -> books.csv
262656 -rw-rw-r-- 2 ada ada 11 Nov 14 14:31 books.csv
$ rm books.csv
$ cat books-hard.csv
Dune,14.99
$ cat books-sym.csv
cat: books-sym.csv: No such file or directory
$ ls -l books-sym.csv
lrwxrwxrwx 1 ada ada 9 Nov 14 14:31 books-sym.csv -> books.csv

İlk sütun inode numarası: books.csv ile books-hard.csv aynı inode'u (262656) paylaşıyor ve link sayısı 2. Orijinal adı silince içerik hard link üzerinden yaşamaya devam etti; symlink ise artık var olmayan bir path'i gösteriyor (dangling link) ve ls onu hâlâ listeliyor. Hard link yalnızca aynı file system içinde ve yalnızca dosyalar için çalışır; klasörlere ve başka disklere symlink gerekir. Deploy'da kullandığımız da bu yüzden symlink. Inode, file descriptor ve page cache'in ayrıntısı İşletim Sistemi Temelleri serisinin file system yazısında.

Şimdi tahminin cevabı:

$ sudo ln -sf /opt/bookstore/releases/2026-11-14-2 /opt/bookstore/current
$ readlink /opt/bookstore/current
/opt/bookstore/releases/2026-11-14-1
$ ls -l /opt/bookstore/releases/2026-11-14-1 | grep -- "->"
lrwxrwxrwx 1 root root    36 Nov 14 14:38 2026-11-14-2 -> /opt/bookstore/releases/2026-11-14-2

Link değişmedi. current bir klasörü gösteren symlink olduğu için ln onu "hedef klasör" saydı ve yeni link'i eski release'in içine, 2026-11-14-2 adıyla oluşturdu. Hata yok, uyarı yok; uygulama eski sürümle çalışmaya devam ediyor ve eski release'in içinde garip bir link duruyor. Çözüm -n (--no-dereference): link'i takip etme, link'in kendisini değiştir.

$ sudo rm /opt/bookstore/releases/2026-11-14-1/2026-11-14-2
$ sudo ln -sfn /opt/bookstore/releases/2026-11-14-2 /opt/bookstore/current
$ readlink /opt/bookstore/current
/opt/bookstore/releases/2026-11-14-2

Değişimin atomik olup olmadığını strace ile kontrol ettim: GNU (Linux'taki temel komut satırı araçlarını geliştiren özgür yazılım projesi) coreutils 9.4'teki ln -sfn link'i önce geçici bir adla oluşturuyor, sonra rename ile current'ın üzerine taşıyor; yani okuyan bir process hiçbir an link'i eksik görmüyor. Web Server'lar ve Hosting serisindeki deploy script'i bunu bir adım daha garantiye alıp ln -sfn ... current.tmp ve ardından mv -T current.tmp current kullanıyordu; bu yol ln'in hangi implementasyonu kullandığından bağımsız çalışır. Geri dönüş de aynı tek komut: link'i eski release'e çevir, service'i yeniden başlat.

Mount Noktaları, /proc ve /sys

Tek ağacın parçaları farklı yerlerden gelir. findmnt hangi klasörün neye bağlı olduğunu gösterir:

$ findmnt -l -o TARGET,SOURCE,FSTYPE | grep -E "^(/|/proc|/sys|/run|/boot) "
/sys                     sysfs       sysfs
/proc                    proc        proc
/run                     tmpfs       tmpfs
/                        /dev/vda1   ext4
/boot                    /dev/vda16  ext4

/ ve /boot diskte, /run bellekte (tmpfs), /proc ve /sys ise hiçbir yerde değil: okuduğun anda kernel tarafından üretiliyorlar. "Linux'ta her şey bir dosyadır" sözü buradan geliyor; process'ler, donanım ve kernel ayarları dosya gibi okunuyor.

$ ls -l /proc/meminfo
-r--r--r-- 1 root root 0 Nov 14 14:04 /proc/meminfo
$ head -3 /proc/meminfo
MemTotal:        2013832 kB
MemFree:         1033912 kB
MemAvailable:    1704368 kB

Boyutu 0 görünen bir dosyadan veri okuduk; free komutu da bu dosyayı okuyor. Daha kullanışlısı, her process'in /proc/<pid>/ altındaki klasörü. Bookstore.Api'yi current üzerinden, ASPNETCORE_ENVIRONMENT=Production ile başlattım ve process'e dışarıdan baktım:

$ PID=$(pgrep -f "^dotnet Bookstore.Api.dll")
$ ls -l /proc/$PID/cwd /proc/$PID/exe
lrwxrwxrwx 1 ada ada 0 Nov 14 14:31 /proc/5748/cwd -> /opt/bookstore/releases/2026-11-14-2
lrwxrwxrwx 1 ada ada 0 Nov 14 14:31 /proc/5748/exe -> /usr/lib/dotnet/dotnet
$ tr "\0" " " < /proc/$PID/cmdline; echo
dotnet Bookstore.Api.dll --urls http://127.0.0.1:5000
$ tr "\0" "\n" < /proc/$PID/environ | grep -E "^(ASPNETCORE_[A-Z]+|HOME|USER)="
ASPNETCORE_ENVIRONMENT=Production
HOME=/home/ada
USER=ada
$ grep -E "^(VmRSS|Threads)" /proc/$PID/status
VmRSS:	   71088 kB
Threads:	20

cwd process'in çalışma klasörü: current'tan başlattık ama process symlink'in çözülmüş hâlini, yani gerçek release klasörünü tutuyor. Bu, link'i çevirdikten sonra çalışan process'in neden eski release'te kaldığını da açıklıyor: yeni release'e geçmek için restart gerekiyor. environ, process başlarken aldığı environment variable'ları gösterir; "uygulama Production'da mı çalışıyor, connection string hangi env variable'dan geldi" sorularının en kesin cevabı burası. Bu dosyayı process'in sahibi ve root okuyabilir, ama bu, environment variable'a koyduğun secret'ların aynı sunucuda root olan herkese açık olduğu anlamına geliyor. /sys ise donanım ve kernel nesnelerini gösteriyor; bir geliştirici olarak oraya daha seyrek bakacaksın. Process'leri dördüncü yazıda /proc üzerinden daha ayrıntılı okuyacağız.

Dosyanın İçine Bakmak

cat dosyayı ekrana basar ve küçük dosyalar için yeterli. Büyük dosyalar için less: ok tuşları ve Space ile gezersin, /hata ile ararsın, G ile sona gidersin, q ile çıkarsın. head -n 20 baştan, tail -n 20 sondan 20 satır verir; tail -f ile log'u canlı izlemeyi altıncı yazıda kullanacağız. Uzantısı yalan söyleyebilecek dosyalar için file, ayrıntılar için stat:

$ cd /opt/bookstore/current
$ file Bookstore.Api Bookstore.Api.dll Bookstore.Api.runtimeconfig.json
Bookstore.Api:                    ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=e5f3d821a5f950a680680dda9429cf9713469963, stripped
Bookstore.Api.dll:                PE32+ executable (console) x86-64 Mono/.Net assembly, for MS Windows, 2 sections
Bookstore.Api.runtimeconfig.json: JSON text data
$ stat Bookstore.Api.dll
  File: Bookstore.Api.dll
  Size: 8192      	Blocks: 16         IO Block: 4096   regular file
Device: 253,1	Inode: 262651      Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/     ada)   Gid: ( 1000/     ada)
Access: 2026-11-14 14:31:45.761240354 +0000
Modify: 2026-11-14 14:19:02.000000000 +0000
Change: 2026-11-14 14:31:21.247058926 +0000
 Birth: 2026-11-14 14:31:21.245058911 +0000

file içeriğin ilk byte'larına bakar. linux-x64 için publish ettiğimizden uzantısız Bookstore.Api bir Linux programı (ELF), yani ./Bookstore.Api diye doğrudan çalıştırılabilir; .dll ise for MS Windows yazsa da her platformda dotnet ile çalışan bir .NET assembly'si. stat'taki üç zamana dikkat: Modify içeriğin değiştiği an (rsync ve cp -a bunu kaynaktan korudu), Change izin ya da sahiplik gibi metadata'nın değiştiği an, Access son okuma. "Bu dosya ne zaman değişti" diye bakarken Modify'a bak.

Üretimde Dikkat

Configuration'ı release klasörünün içinde tutma. /opt/bookstore/releases/2026-11-14-2 bir sonraki deploy'da eski olacak ve birkaç release sonra silinecek; production connection string'i ya da ortama özel bir ayar oradaysa her deploy'da kaybolur ya da yanlış sürümle gelir. Ortama özel her şey /etc/bookstore/'da durur ve uygulamaya environment variable ya da systemd'nin EnvironmentFile= satırıyla verilir. Aynı mantık veride de geçerli: uygulamanın yazdığı her şey /var/lib/bookstore/'a gitmeli, release klasörü salt okunur kalmalı. Böylece bir release'i silmek ya da geri dönmek hiçbir veriyi etkilemez.

Eski release'leri temizle ama hepsini değil. Son iki üç release'i tutmak hızlı geri dönüş için yeterli; gerisi diski doldurur. ls -dt /opt/bookstore/releases/* klasörleri en yeniden eskiye sıralar, silmeyi bunun üstüne kurarsın; bunu bir script'e dökmek Bash Scripting serisinin işi.

Sık Düşülen Tuzaklar

Büyük/küçük harf. Windows'un file system'i harf farkını görmezden gelir, Linux'unki görmez. Windows'ta sorunsuz çalışan bir projede dosya adı appsettings.production.json diye yazılmışsa ASP.NET Core Linux'ta onu bulamaz ve hiçbir uyarı vermez. Bu dosyaya Urls ayarı koyup denedim:

$ cat appsettings.production.json
{
  "Urls": "http://127.0.0.1:5080"
}
$ ASPNETCORE_ENVIRONMENT=Production dotnet Bookstore.Api.dll
      Now listening on: http://localhost:5000
$ mv appsettings.production.json appsettings.Production.json
$ ASPNETCORE_ENVIRONMENT=Production dotnet Bookstore.Api.dll
      Now listening on: http://127.0.0.1:5080

İlk çalıştırmada dosya yok sayıldı ve uygulama varsayılan adrese bind etti; dosya adı ortam adıyla birebir aynı olunca ayar geldi. Web Server'lar ve Hosting serisinin systemd yazısında aynı tuzağı connection string üzerinden görmüştük. Aynı kural import'larda, Angular asset path'lerinde ve Dockerfile'daki COPY satırlarında da geçerli.

ln -sf ile klasör symlink'ini çevirmeye çalışmak. Yukarıda gördüğümüz gibi hata vermeden yanlış yerde link oluşturur. Klasör gösteren link'ler için her zaman -n.

Dosya adında boşluk ve Windows'tan gelen satır sonları. Bookstore Reports.csv gibi bir ad komut satırında iki argümana bölünür; tırnak içine al. Windows'ta yazılıp sunucuya kopyalanan bir shell script'i çalıştırınca bash 5.2 yalnızca cannot execute: required file not found diyor; dosya orada olduğu için mesaj yanıltıcı. Sebep CRLF (Windows'un iki karakterlik satır sonu) satır sonları: ilk satırdaki interpreter /bin/bash\r olarak okunuyor. file bunu "with CRLF line terminators" diye gösterir, sed -i 's/\r$//' deploy.sh düzeltir.

/tmp'ye kalıcı bir şey koymak. Reboot'ta silinir. Uygulamanın tekrar kullanacağı her şey /var/lib/bookstore'a.

Küçük Bir Deneme

★☆☆ Kendi VM'inde /opt/bookstore, /etc/bookstore, /var/log/bookstore ve /var/lib/bookstore klasörlerini oluştur, Bookstore.Api'yi iki ayrı release klasörüne kopyala ve current'ı ln -sfn ile ikisi arasında çevir. Her seferinde readlink ile kontrol et.

★★☆ Uygulamayı current üzerinden başlat, /proc/<pid>/cwd'ye bak, sonra current'ı diğer release'e çevir ve cwd'ye tekrar bak. Uygulama hangi release'te çalışıyor? /proc/<pid>/environ'dan ASPNETCORE_ENVIRONMENT'ı oku.

★★★ sudo find / -xdev -type f -newer /etc/hostname ile VM'i kurduktan sonra değişen dosyaları listele ve /etc, /var ve /home altındakileri ayır. Hangileri configuration, hangileri değişken veri? Çıktıyı wc -l ile say ve aynı komutu bir paket kurduktan sonra tekrar çalıştır.

Bu yazıda dosyaları yerleştirdik ama hepsi hâlâ ya root'a ya da ada'ya ait ve uygulamayı kendi kullanıcımızla çalıştırıyoruz. Bir sonraki yazıda user, group ve permission'ları ele alıp Bookstore için ayrı bir service kullanıcısı açacağız, deploy kullanıcısını ondan ayıracağız ve /etc/bookstore/secrets.env'i yalnızca okuması gerekenin okuyabileceği hâle getireceğiz.