---
title: "Persistent Volume di Helipod: Simpan File yang Tidak Hilang saat Redeploy"
url: "https://helipod.io/blog/persistent-volume-storage-helipod"
date: "2026-05-14"
updated: "2026-05-14"
author: "Tim Helipod"
tags: ["storage", "persistent volume", "helipod", "laravel", "files", "database"]
excerpt: "Cara pakai persistent volume di Helipod agar file tidak hilang saat redeploy: mount path, Laravel storage, SQLite, upload, vs ephemeral storage."
---

# Persistent Volume di Helipod: Simpan File yang Tidak Hilang saat Redeploy

Setiap kali kamu redeploy aplikasi di Helipod, container lama dihapus dan container baru dibuat dari image terbaru. Ini bagus untuk konsistensi, tapi punya konsekuensi: **semua file yang dibuat di dalam container akan hilang**.

Upload foto dari user? Hilang. Cache yang sudah dibangun? Hilang. Database SQLite? Hilang.

Solusinya adalah **Persistent Volume**: storage yang tetap ada bahkan setelah container dihapus dan dibuat ulang, dan di-mount ke container di path yang kamu tentukan.

## Ephemeral vs Persistent Storage

**Ephemeral Storage (default):** File yang dibuat di dalam container disimpan di layer container. Saat container dihapus (setiap redeploy), file-file ini ikut hilang.

**Persistent Volume:** Storage terpisah yang di-mount ke container. Tidak ikut hilang saat container dihapus. Tersedia lagi di container baru saat redeploy.

Analoginya: container seperti kamar hotel, setiap kamu check out, semua yang ada di kamar dibersihkan. Persistent volume seperti loker penyimpanan di lobby, tetap ada meski kamu ganti kamar.

## Cara Setup Persistent Volume

### Via Dashboard

1. Buka service di dashboard Helipod
2. Buka tab **Settings**
3. Scroll ke **Storage Volumes → Mounted Volumes**
4. Klik **+ Add Volume**
5. Isi:
   - **Mount Path**: path di dalam container tempat volume di-mount
   - **Size**: ukuran storage yang dibutuhkan (GB)
6. Klik Save dan Redeploy

### Via helipack.json

```json
{
  "volume": {
    "mountPath": "/app/storage",
    "size": 10
  }
}
```

> **Penting:** Setelah volume di-mount, isi volume **tidak akan hilang** saat redeploy. Tapi jika kamu **menghapus volume**, semua data hilang permanen dan tidak bisa dipulihkan.

## Use Case: Laravel Storage

Laravel menyimpan uploaded files, generated files, dan cache di folder `storage/`. Secara default, ini ephemeral. Untuk persistent:

### Setup

Di **Settings → Storage Volumes → Add Volume:**
- Mount Path: `/var/www/html/storage`
- Size: 10 GB (atau sesuai kebutuhan)

Atau di `helipack.json`:
```json
{
  "volume": {
    "mountPath": "/var/www/html/storage",
    "size": 10
  }
}
```

### Konfigurasi Laravel

```env
# .env
FILESYSTEM_DISK=local
```

```php
// config/filesystems.php
'disks' => [
    'local' => [
        'driver' => 'local',
        'root' => storage_path('app'),
        'throw' => false,
    ],
    'public' => [
        'driver' => 'local',
        'root' => storage_path('app/public'),
        'url' => env('APP_URL').'/storage',
        'visibility' => 'public',
        'throw' => false,
    ],
],
```

### Setup Storage Link

Setelah volume di-mount dan deploy pertama, jalankan dari terminal:

```bash
php artisan storage:link
```

Ini membuat symlink dari `public/storage` ke `storage/app/public`.

> **Catatan:** `storage:link` perlu dijalankan ulang setiap kali ada redeploy karena symlink ada di container layer yang ephemeral, bukan di volume. Tambahkan ke `helipack.json` untuk otomatis:

```json
{
  "run": {
    "before": "php artisan storage:link && php artisan migrate --force"
  },
  "volume": {
    "mountPath": "/var/www/html/storage",
    "size": 10
  }
}
```

### Upload File di Laravel

```php
// Controller
public function upload(Request $request)
{
    $request->validate([
        'file' => 'required|file|max:10240',
    ]);
    
    $path = $request->file('file')->store('uploads', 'public');
    
    return response()->json([
        'path' => $path,
        'url' => Storage::url($path),
    ]);
}
```

File akan disimpan di `storage/app/public/uploads/`: yang ada di persistent volume, tidak hilang saat redeploy.

## Use Case: Django Media Files

Django menyimpan uploaded files di `MEDIA_ROOT`. Untuk persistent:

Mount path: `/app/media`

```python
# settings.py
import os

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
```

```python
# urls.py (untuk serve media di development/production sederhana)
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    # ... url patterns
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
```

```python
# models.py
from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=200)
    image = models.ImageField(upload_to='products/')  # tersimpan di media/products/
```

> **Untuk production yang serius:** Pertimbangkan untuk serve media files via object storage (AWS S3, DigitalOcean Spaces, Cloudflare R2) menggunakan `django-storages`: lebih scalable dan tidak tergantung persistent volume.

## Use Case: SQLite Database

SQLite cocok untuk aplikasi kecil atau yang tidak membutuhkan PostgreSQL. Karena database adalah satu file, harus ada di persistent volume agar tidak hilang.

Mount path: `/app/data`

**Django:**
```python
# settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': '/app/data/db.sqlite3',  # di dalam persistent volume
    }
}
```

**FastAPI + SQLAlchemy:**
```python
DATABASE_URL = "sqlite+aiosqlite:////app/data/myapp.db"
engine = create_async_engine(DATABASE_URL)
```

**Flask:**
```python
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////app/data/myapp.db'
```

> **Catatan:** SQLite tidak cocok untuk multi-replica deployment karena tidak mendukung concurrent writes dari multiple processes. Jika butuh replicas, gunakan PostgreSQL.

## Use Case: Node.js / Next.js Cache

Beberapa aplikasi Node.js menyimpan cache di disk:

**Next.js ISR Cache:**
```json
{
  "volume": {
    "mountPath": "/app/.next/cache",
    "size": 5
  }
}
```

Ini membuat Next.js Incremental Static Regeneration cache persist antar redeploy, mempercepat load time setelah deployment.

**Puppeteer / Playwright Browser Cache:**
```json
{
  "volume": {
    "mountPath": "/root/.cache/puppeteer",
    "size": 2
  }
}
```

## Use Case: Python / ML Model Files

Untuk ML model serving, model files sering berukuran besar (100MB, 10GB). Simpan di persistent volume agar tidak perlu download ulang setiap redeploy.

```json
{
  "volume": {
    "mountPath": "/app/models",
    "size": 20
  },
  "run": {
    "before": "python scripts/download_model.py"
  },
  "health": {
    "path": "/health",
    "duration": 60
  }
}
```

```python
# scripts/download_model.py
import os
from pathlib import Path

MODEL_PATH = Path("/app/models/model.bin")

if not MODEL_PATH.exists():
    print("Downloading model...")
    # download dari HuggingFace, S3, atau URL lain
    import urllib.request
    urllib.request.urlretrieve(
        "https://huggingface.co/yourmodel/model.bin",
        MODEL_PATH
    )
    print("Model downloaded!")
else:
    print("Model already exists, skipping download.")
```

Dengan pola ini, model hanya di-download saat volume kosong (deploy pertama atau setelah volume di-reset). Redeploy berikutnya langsung pakai model yang sudah ada di volume.

## Backup Data di Persistent Volume

Helipod tidak menyediakan automatic backup untuk persistent volume saat ini. Kamu perlu setup backup strategy sendiri.

**Opsi 1: Backup ke Object Storage (Direkomendasikan)**

```bash
# Dari terminal Helipod, install rclone
# atau gunakan aws s3 sync / s3cmd

# Contoh backup ke DigitalOcean Spaces
s3cmd sync /app/storage/ s3://mybucket/backups/$(date +%Y%m%d)/

# Atau ke Cloudflare R2
aws s3 sync /app/storage/ s3://mybucket/backups/ \
  --endpoint-url https://xxx.r2.cloudflarestorage.com
```

**Opsi 2: PostgreSQL Dump (untuk data penting)**

Jangan simpan data penting hanya di SQLite file. Untuk data yang critical, gunakan PostgreSQL dengan backup rutin:

```bash
# Dari terminal
pg_dump $DATABASE_URL | gzip > /app/data/backup_$(date +%Y%m%d).sql.gz
```

## Troubleshooting

**Volume tidak ter-mount / path tidak ada**

Cek apakah volume sudah berhasil ditambahkan di Settings. Dari terminal:
```bash
df -h | grep volume
mount | grep /app/storage
ls -la /app/storage/
```

**Permission denied saat menulis ke volume**

Volume di-mount dengan permission root. Jika aplikasi berjalan sebagai non-root user (best practice Helipod), perlu set permission:

```bash
# Dari terminal
sudo chown -R appuser:appgroup /app/storage
# Atau
chmod -R 777 /app/storage  # kurang aman, tapi cepat untuk debug
```

Solusi lebih permanen: tambahkan di `helipack.json`:
```json
{
  "run": {
    "before": "chown -R www-data:www-data /var/www/html/storage && php artisan migrate --force"
  }
}
```

**Data hilang padahal sudah setup volume**

Pastikan mount path di Settings **sama persis** dengan path yang dipakai aplikasi. Perbedaan trailing slash atau typo bisa menyebabkan aplikasi menulis ke path yang salah (ephemeral layer, bukan volume).

```bash
# Verifikasi dari terminal
ls -la /app/storage/  # harus ada file yang kamu upload sebelumnya
```

## Keterbatasan Persistent Volume

**Single-pod only:** Volume di-mount ke satu pod. Jika kamu menjalankan multiple replicas, semua replica akan mount volume yang sama, ini bisa menyebabkan konflik jika ada concurrent writes. Untuk multi-replica deployment, gunakan object storage (S3/R2/Spaces) sebagai ganti filesystem local.

**Not for databases dengan high write:** Volume persistent cocok untuk SQLite dengan traffic rendah. Untuk PostgreSQL atau database lain dengan write yang sering, gunakan pod database dedicated yang sudah dioptimasi.

**Manual backup:** Tidak ada automatic snapshot atau point-in-time recovery. Setup backup sendiri untuk data yang critical.

## Kesimpulan

Persistent Volume di Helipod menyelesaikan masalah klasik container: "file hilang saat redeploy." Untuk uploaded files, ML models, SQLite databases, dan cache yang butuh persist, mount volume di path yang tepat dan selesai.

Untuk aplikasi yang butuh scalability lebih tinggi (multi-replica), pertimbangkan pindah file storage ke object storage eksternal sambil menggunakan PostgreSQL untuk data terstruktur.

Ingin tahu lebih tentang deploy PostgreSQL di Helipod? Baca [Panduan Deploy PostgreSQL di Helipod](/blog/deploy-postgresql-di-helipod).

Punya pertanyaan? Hubungi kami di [support@helipod.io](mailto:support@helipod.io) atau bergabung ke komunitas di [hangar.helipod.io](https://hangar.helipod.io).
