# Network Storage

The Network Storage service provides a shared [mount](https://fixed.docs.upsun.com/create-apps/image-properties/mounts.md) so that apps in a multi-app setup can share files on a centralized file system instead of a local directory.

Your apps can use any combination of `local` and `service` mounts.

**Note**: 

Writing to network mounts is slightly slower than to local mounts, most noticeably during high-volume sequential file creation — in that case, a local mount is more effective. Applications polling network mounts for a new file may not immediately detect it even though it exists.

Detection can be delayed by approximately 30 seconds, so polling logic should account for this delay and not treat a missing file as an error until that window has passed.

This service is the Upsun Fixed network storage implementation, not the version of a third-party application.

## Supported versions

You can select the major and minor version.

Patch versions are applied periodically for bug fixes and security updates.
When you deploy your app, you always get the latest available patches.

| Grid | Dedicated Gen 2 |
| 

   - 1.0

  None available

**Note**: 

To ensure optimal stability and throughput, **version 1.0 remains the recommended standard for all environments.** If you are using a different version, refer to the [migration options](#migration-options) below to migrate to version 1.0.

### Deprecated versions

The following versions are [deprecated](https://fixed.docs.upsun.com/glossary.md#deprecated-versions).
They're available, but they don't receive security updates from upstream and aren't guaranteed to work.
They'll be removed in the future – consider migrating to a [supported version](#supported-versions).

| Grid | Dedicated Gen 2 |
| 

   - 2.0

  None available

**Note**: 

Upsun has deprecated version 2.0 due to performance inconsistencies identified under production-level workloads.
**We strongly advise migrating any services currently using version 2.0 back to version 1.0 to avoid potential performance degradation.** See the [migration options](#migration-options) below.

### Migration options {#migration-options}

**Migrating to version 1.0**: 

Changing the service version to ``1.0`` directly in your configuration will trigger a reinitialization, creating a fresh volume and **permanently deleting all existing data**.

To prevent unintended data loss and minimize downtime, **Upsun recommends a manual migration or reaching out to our Support team for guidance.** These options are described below.
**Important Considerations**

 - **Back up your data:** Regardless of the migration method you choose, [manually back up your environment](https://fixed.docs.upsun.com/environments/backup.md#create-a-manual-backup) before starting. **Data cannot be recovered once the service removal begins.**

 - **Code safety:** This process affects only data within the ``network-storage`` service. Your Git-based application code and environment variables remain unaffected.

**Option 1: Manual migration to version 1.0**
Complete these steps only if you understand how to provision and decommission Upsun Fixed services and prefer to complete the transition yourself. 

1. [Manually back up your environment](https://fixed.docs.upsun.com/environments/backup.md#create-a-manual-backup).

1. Provision a new service: Add a second `network-storage` service to your configuration and set its version to `1.0`.

1. Sync your data: Manually copy your persistent data from the old volume to the new `1.0` volume.

1. Decommission the old service: After you verify the data, remove the old version of the service from your configuration.

**Option 2: Supported Transition**
If you are uncomfortable performing the manual migration steps, please **[create a Support ticket](https://fixed.docs.upsun.com/learn/overview/get-support.md)**. Our team will provide guidance to ensure your data remains intact. 

## Usage example

### 1. Configure the service

To define the service, use the `network-storage` type:

```yaml  {location=".platform/services.yaml"}
# The name of the service container. Must be unique within a project.
<SERVICE_NAME>:
  type: network-storage:<VERSION>
  disk: 256
```

`<SERVICE_NAME>` must be [RFC 1123](https://tools.ietf.org/html/rfc1123) compliant, and as such it must:

- Contain at most 63 characters
- Contain only lowercase alphanumeric characters or `-` (underscores `_` are not allowed)
- Start with an alphanumeric character
- End with an alphanumeric character

This is due to the fact that `<SERVICE_NAME>` is used as hostname for the network storage.

Note that changing the name of the service replaces it with a brand new service and all existing data is lost.
Back up your data before changing the service.

### 2. Add the mount

To define the mount accessible by your application, use the following configuration:

```yaml  {location=".platform.app.yaml"}
name: myapp
mounts:
  <TARGET_PATH>:
    source: service
    service: <SERVICE_NAME>
    source_path: <SOURCE_PATH>
```

- `<TARGET_PATH>` is the path to your mount within the app container (relative to the app’s root).
- `<SERVICE_NAME>` is the name you [defined in step 1](#1-configure-the-service).
- `<SOURCE_PATH>` specifies where the mount points inside the service. 
  If the `source_path` is an empty string (`""`), your mount points to the entire service. 
  If you don’t define a `source_path`, Upsun Fixed uses the `MOUNT_PATH` as default value, without leading or trailing slashes.
  For example, if your mount lives in the `/my/files/` directory within your app container, it will point to a `my/files` directory within the service.

### Example configuration

### [Service definition](https://fixed.docs.upsun.com/add-services.md)

```yaml  {location=".platform/services.yaml"}
# The name of the service container. Must be unique within a project.
network-storage:
    type: network-storage:1.0
    disk: 256
```

#### [App configuration](https://fixed.docs.upsun.com/create-apps.md)

```yaml  {location=".platform.app.yaml"}
name: myapp
mounts:
  'my/files':
    source: service
    service: network-storage
    source_path: files
```

## Multi-application usage

If your project contains [multiple apps](https://fixed.docs.upsun.com/create-apps/multi-app.md), they can all use the same network mounts.
If the `source_path` is the same for both apps,
the files are shared between the two applications even if the mount location is different.

It's also possible to have one app mount a `source_path` that's a subdirectory of another application's mount.
For example:

```yaml  {location=".platform/applications.yaml"}
# The name of the app container. Must be unique within a project.
app1:
  # The location of the application's code.
  source:
    root: "app1"

  [...]

  mounts:
    # The path to your mount within the app container (relative to the app's root).
    'web/uploads':
      # Specifies that the mount points to a network storage service that can be shared between apps.
      source: service
      # The name of the network storage service the mount points to.
      service: network-storage
      # Specifies where your mount points inside the external directory that is mounted to your app container.
      source_path: uploads

# The name of the app container. Must be unique within a project.
app2:
  # The location of the application's code.
  source:
    root: "app2"

  [...]

  mounts:
    # The path to your mount within the app container (relative to the app's root).
    'process':
      # Specifies that the mount points to a network storage service that can be shared between apps.
      source: service
      # The name of the network storage service the mount points to.
      service: network-storage
      # Specifies where your mount points inside the external directory that is mounted to your app container.
      # Since the target is the uploads directory app1's mount already points to,
      # the network storage service is effectively shared between app1 and app2.
      source_path: uploads/incoming

    # The path to your mount within the app container (relative to the app's root).
    'done':
      # Specifies that the mount points to a network storage service that can be shared between apps.
      source: service
      # The name of the network storage service the mount points to.
      service: network-storage
      # Specifies where your mount points inside the external directory that is mounted to your app container.
      # Since the target is the uploads directory app1's mount already points to,
      # the network storage service is effectively shared between app1 and app2.
      source_path: uploads/done
```

In this example, `app1` has access to the entire `uploads` directory by writing to `web/uploads`.
`app2` has two mounts that it can write to: `process` and `done`.
The `process` mount refers to the same directory as the `web/uploads/incoming` directory does on `app1`,
and the `done` mount refers to the same directory as the `web/uploads/done` directory on `app1`.

## Worker instances

When defining a [worker](https://fixed.docs.upsun.com/create-apps/image-properties/workers.md) instance,
keep in mind what mount behavior you want.

`local` mounts are a separate storage area for each instance,
while `service` mounts can be shared between instances.

For example, you can define a network storage service called `files`:

```yaml  {location=".platform/services.yaml"}
# The name of the network storage service. Must be unique within a project.
files:
    type: network-storage:1.0
    disk: 2048
```

You can then use this service to  define a `network_dir` network mount and a `local_dir` local mount,
to be used by a `web` instance and a `queue` worker instance:

```yaml  {location=".platform/applications.yaml"}
myapp:
  # The type of the application to build.
  type: "nodejs:20"

  # Defines how much space is available to local mounts.
  disk: 512

  mounts:
    # Defines a network storage mount that can be shared by both worker instances.
    'network_dir':
      source: service
      service: files
      source_path: our_stuff

    # Defines a local mount that's available to each instance separately.
    'local_dir':
      source: local
      source_path: my_stuff

  # Defines a web instance.
  web:
    locations:
      "/":
        root: "public"
        passthru: true
        index: ['index.html']

  # Define a queue worker instance from the same code but with a different start.
  workers:
    queue:
      commands:
        start: ./start.sh
```

Both the web instance and the `queue` worker have two mount points:

- The `local_dir` mount on each is independent and not connected to each other at all
  and they *each* take 1024 MB of space.
- The `network_dir` mount on each points to the same network storage space on the `files` service.
  They can both read and write to it simultaneously.
  The amount of space it has available depends on the `disk` key specified for the service configuration (in this case, 2048 MB).

## How do I give my workers access to my main application's files?

The most common use case for `network-storage` is to allow a CMS-driven site to use a worker that has access to the same file mounts as the web-serving application.
For that case, all that's needed is to set the necessary file mounts as `service` mounts.

For example, the following `.platform.app.yaml` file (fragment) keeps Drupal files directories shared between web and worker instances while keeping the Drush backup directory web-only (as it has no need to be shared).
(This assumes a Network Storage service named `files` has also been defined in `.platform/services.yaml`.)

```yaml  {location=".platform/applications.yaml"}
myapp:
  # The type of the application to build.
  type: "php:8.5"

  relationships:
    mariadb:

  disk: 1024

  mounts:
    # The public and private files directories are
    # network mounts shared by web and workers.
    'web/sites/default/files':
      source: service
      service: files
      source_path: files
    'private':
      source: service
      service: files
      source_path: private
    # The backup, temp, and cache directories for
    # Drupal's CLI tools don't need to be shared.
    # It wouldn't hurt anything to make them network
    # shares, however.
    '/.drush':
      source: local
      source_path: drush
    'tmp':
      source: local
      source_path: tmp
    'drush-backups':
      source: local
      source_path: drush-backups
    '/.console':
      source: local
      source_path: console

  # Crons run on the web container, so they have the
  # same mounts as the web container.
  crons:
    drupal:
      spec: '*/20 * * * *'
      commands:
        start: 'cd web ; drush core-cron'

  # The worker defined here also has the same 6 mounts;
  # 2 of them are shared with the web container,
  # the other 4 are local to the worker.
  workers:
    queue:
      commands:
        start: |
          cd web && drush queue-run myqueue
```

## How can I migrate a local storage to a network storage?

There is no automated way of transferring data from one storage type to another.
However, the process is fundamentally "just" moving files around on disk, so it's reasonably straightforward.

Suppose you have this mount configuration:

```yaml  {location=".platform.app.yaml"}
name: myapp
mounts:
  web/uploads:
    source: local
    source_path: uploads
```

And want to move that to a network storage mount.
The following approximate steps do so with a minimum of service interruption.

1. Add a new `network-storage` service, named `files`,
   that has at least enough space for your existing files with some buffer.

   ```yaml  {location=".platform/services.yaml"}
   # The name of the service container. Must be unique within a project.
   files:
       type: network-storage:1.0
       disk: 1024
   ```

2. Add a new mount (`new-uploads`) to the network storage service on a non-public directory.
   (Remember the `source_path` can be the same since they're on different storage services.)

   ```yaml  {location=".platform.app.yaml"}
   mounts:
     web/uploads:
       source: local
       source_path: uploads
     new-uploads:
       source: service
       service: files
       source_path: uploads
   ```

3. Deploy these changes.
   Then use `rsync` to copy all files from the local mount to the network mount.
   (Be careful of the trailing `/`.)

    ```bash
    rsync -avz web/uploads/* new-uploads/
    ```

4. Reverse the mounts.
   Point the `web/uploads` directory to the network mount instead.
   Commit and push the change, testing to make sure the network files are accessible.

   ```yaml  {location=".platform.app.yaml"}
   mounts:
     old-uploads:
       source: local
       source_path: uploads
     web/uploads:
       source: service
       service: files
       source_path: uploads
   ```

5. Clean up.
   First, run another `rsync` just to make sure any files uploaded during the transition aren't lost.
   (Note the command is different here.)

   ```bash
   rsync -avz old-uploads/* web/uploads/
   ```

   Once you're confident all the files are accounted for, delete the entire contents of `old-uploads`.
   If you don't, the files remain on disk but inaccessible, just eating up disk space needlessly.

   Once that's done you can remove the `old-uploads` mount and push again to finish the process
   You are also free to reduce the `disk` size in the `.platform.app.yaml` file if desired,
   but make sure to leave enough for any remaining local mounts.

