> ## Documentation Index
> Fetch the complete documentation index at: https://gcore.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Edge Storage

export const MethodSection = ({children}) => children ?? null;

export const MethodSwitch = ({children}) => {
  const tabs = React.Children.toArray(children).map(c => {
    if (!c || !c.props) return null;
    if (c.props.id) return c;
    const inner = c.props.children;
    if (inner && inner.props && inner.props.id) return inner;
    return null;
  }).filter(Boolean);
  const firstId = tabs.length > 0 ? tabs[0].props.id : "";
  const [active, setActive] = React.useState(firstId);
  React.useEffect(() => {
    try {
      const saved = localStorage.getItem("gcore_docs_method");
      if (saved && tabs.find(t => t.props.id === saved)) {
        setActive(saved);
      }
    } catch (_) {}
  }, []);
  React.useEffect(() => {
    try {
      document.querySelectorAll("h2[id], h3[id]").forEach(heading => {
        const visible = heading.offsetParent !== null;
        document.querySelectorAll(`a[href="#${heading.id}"]`).forEach(link => {
          if (link.closest("h1,h2,h3,h4,h5,h6")) return;
          const li = link.closest("li");
          if (li) li.style.display = visible ? "" : "none";
        });
      });
    } catch (_) {}
    window.dispatchEvent(new Event("scroll"));
  }, [active]);
  const handleClick = id => {
    setActive(id);
    try {
      localStorage.setItem("gcore_docs_method", id);
    } catch (_) {}
  };
  return <div>
      <div className="not-prose flex gap-0 border-b border-zinc-200 dark:border-zinc-800 mb-8 mt-2" role="tablist">
        {tabs.map(tab => {
    const isActive = active === tab.props.id;
    return <button key={tab.props.id} role="tab" aria-selected={isActive} onClick={() => handleClick(tab.props.id)} className={["px-4 py-2 text-sm font-medium border-b-2 -mb-px transition-colors cursor-pointer", isActive ? "border-primary text-primary" : "border-transparent text-zinc-500 hover:text-zinc-800 dark:hover:text-zinc-200"].join(" ")}>
              {tab.props.label}
            </button>;
  })}
      </div>

      {tabs.map(tab => <div key={tab.props.id} style={{
    display: active === tab.props.id ? "" : "none"
  }}>
          {tab.props.children}
        </div>)}
    </div>;
};

<MethodSwitch>
  <MethodSection id="portal" label="Customer Portal">
    Edge Storage stores key-value data that FastEdge applications can read at runtime.

    <p>Each store is a named container for key-value pairs. After creating a store, link it to a FastEdge application before the application can access its data — linking instructions are in [Managing applications](/fastedge/manage-apps#edge-storage).</p>

    <p>Data is replicated across Gcore's edge network, so reads are served from a local copy at the edge location handling each request.</p>

    ## Create a store

    <p>Creating a store uses a two-step wizard. The first step sets the store name; the second step adds key-value pairs before saving.</p>

    <p>To start, open the [Gcore Customer Portal](https://portal.gcore.com/accounts/reports/dashboard), navigate to **FastEdge**, and select **Edge Storage** in the sidebar.</p>

    1. Click **Add Edge Storage** in the top-right corner.

    <Frame>
      <img src="https://mintcdn.com/gcore/ya83svzwxRUsGLmD/images/docs/fastedge/kv-stores/manage-kv-store/add-store.png?fit=max&auto=format&n=ya83svzwxRUsGLmD&q=85&s=4938b6ace0add086cdcfe17d56ad0892" alt="Edge Storage list with Add Edge Storage button highlighted" width="1480" height="385" data-path="images/docs/fastedge/kv-stores/manage-kv-store/add-store.png" />
    </Frame>

    2. Enter a name and optionally a description.

    3. Click **Create Edge Storage** to proceed to the second step.

    <Frame>
      <img src="https://mintcdn.com/gcore/ya83svzwxRUsGLmD/images/docs/fastedge/kv-stores/manage-kv-store/create-store.png?fit=max&auto=format&n=ya83svzwxRUsGLmD&q=85&s=ce273adc50b1c657f7fc893ec4c2d0fe" alt="Create Edge Storage form with name and description fields" width="1268" height="470" data-path="images/docs/fastedge/kv-stores/manage-kv-store/create-store.png" />
    </Frame>

    4. In the second step, add key-value pairs using the **Insert item** button, or skip this step — pairs can be added later by opening the store from the list.

    5. Click **Save Edge Storage**.

    <p>The store appears in the Edge Storage list.</p>

    ## Insert key-value pairs

    <p>Key-value pairs are added through the **Insert item** panel inside the store view. Pairs can be added during the creation wizard (step 2) or at any time by clicking the store name in the Edge Storage list to open it.</p>

    ### Add a pair manually

    <p>Click **Insert item** and select **KV pair**. In the panel that opens, enter the key and value. Click **+ Add value** to add another row. Click **Save** to apply the changes.</p>

    <p>Keys are limited to 256 bytes.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/ya83svzwxRUsGLmD/images/docs/fastedge/kv-stores/manage-kv-store/insert-pair.png?fit=max&auto=format&n=ya83svzwxRUsGLmD&q=85&s=8850525c61ddfb2642f86a2f7eed63e6" alt="Insert item dropdown with KV pair selected" width="1257" height="582" data-path="images/docs/fastedge/kv-stores/manage-kv-store/insert-pair.png" />
    </Frame>

    <Frame>
      <img src="https://mintcdn.com/gcore/ya83svzwxRUsGLmD/images/docs/fastedge/kv-stores/manage-kv-store/first-pair.png?fit=max&auto=format&n=ya83svzwxRUsGLmD&q=85&s=d2d7d64f13a18b532678df47e0f1e226" alt="Side panel with key-value input rows" width="837" height="269" data-path="images/docs/fastedge/kv-stores/manage-kv-store/first-pair.png" />
    </Frame>

    ### Upload a value from a file

    <p>To load a value from a file instead of typing it, click the upload icon in the value row. Text and binary files are supported — images, fonts, and similar assets. The maximum file size is 1 MB. After uploading, the portal displays a hash of the file content rather than the original filename.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/ya83svzwxRUsGLmD/images/docs/fastedge/kv-stores/manage-kv-store/upload-pair.png?fit=max&auto=format&n=ya83svzwxRUsGLmD&q=85&s=32f5ea9a5ff4f0cbdf7ac6126e6b69c5" alt="Upload icon in the key-value pair row" width="838" height="285" data-path="images/docs/fastedge/kv-stores/manage-kv-store/upload-pair.png" />
    </Frame>

    ### Set an expiration date

    <p>To assign an expiration to a value, click the expiry icon in the value row and select a date and time. After that point, the value stops being returned to applications. It may remain visible in the portal briefly after expiration.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/ya83svzwxRUsGLmD/images/docs/fastedge/kv-stores/manage-kv-store/expire-pair.png?fit=max&auto=format&n=ya83svzwxRUsGLmD&q=85&s=5a91eb156e0e96d62077e7d7566a7824" alt="Expiry icon in the key-value pair row" width="833" height="417" data-path="images/docs/fastedge/kv-stores/manage-kv-store/expire-pair.png" />
    </Frame>

    ## Edit a key-value pair

    <p>Once pairs are in a store, individual values can be updated at any time. Clicking a store name in the Edge Storage list opens the **Edit Edge Storage** page where all pairs are listed. To edit a pair, click the three-dot icon next to it, select **Edit**, update the value or expiration date, then click **Save**.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/ya83svzwxRUsGLmD/images/docs/fastedge/kv-stores/manage-kv-store/edit-store.png?fit=max&auto=format&n=ya83svzwxRUsGLmD&q=85&s=9044f950b76199b1f7880606e5049bd3" alt="Three-dot menu with Edit option highlighted" width="1247" height="704" data-path="images/docs/fastedge/kv-stores/manage-kv-store/edit-store.png" />
    </Frame>

    <Info>
      Additional pairs can be inserted from the edit view. Submitting a key that already exists in the store overwrites its current value.
    </Info>

    ## Delete key-value pairs

    <p>To remove outdated or incorrect data, pairs can be deleted individually or in bulk. Open the store by clicking its name in the Edge Storage list to access both options.</p>

    <p>To delete a single pair, click the three-dot icon next to it, select **Delete**, and confirm.</p>

    <p>To delete multiple pairs, select the checkboxes in the first column of the table, click **Group actions**, and select **Delete all**.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/ya83svzwxRUsGLmD/images/docs/fastedge/kv-stores/manage-kv-store/bulk-delete.png?fit=max&auto=format&n=ya83svzwxRUsGLmD&q=85&s=5a27984f09951f2230da5d5f3ed851bd" alt="Pairs selected with checkboxes and Group actions menu open" width="1241" height="1133" data-path="images/docs/fastedge/kv-stores/manage-kv-store/bulk-delete.png" />
    </Frame>

    ## BYOD: Bring Your Own Database

    <p>BYOD connects a FastEdge store to an external key-value database instead of Gcore's hosted storage. Applications access a BYOD store through the same [JavaScript SDK](https://g-core.github.io/FastEdge-sdk-js/reference/fastedge/kv/open/) or [Rust SDK](https://docs.rs/fastedge/latest/fastedge/key_value/struct.Store.html) as a regular store — no code changes required.</p>

    <p>Any store implementing the Redis protocol is supported: Redis, [Apache Kvrocks](https://kvrocks.apache.org/), and other Redis-compatible key-value stores.</p>

    <p>The external store must be publicly accessible. Gcore's edge nodes connect to it over the network on each request.</p>

    ## Hosted Edge Storage vs BYOD

    <p>With hosted Edge Storage, data lives at every edge location globally and reads are served locally — latency is typically in the single-digit milliseconds. With BYOD, each read is a live query to the external database, so response time depends on the network distance between the edge location and the database host.</p>

    <p>BYOD is a practical choice for development, testing, or workloads where read speed is not critical. For latency-sensitive production workloads, hosted Edge Storage is recommended.</p>

    <p>Supported URL schemes:</p>

    * `redis://` — standard Redis protocol
    * `rediss://` — Redis protocol over TLS

    <p>URL format: `redis://:password@host:port/db-index` or `redis://username:password@host:port/db-index`</p>

    ## Create a BYOD store

    <p>Follow steps 1–3 from [Create a store](#create-a-store), then:</p>

    4. Select the **BYOD: Bring Your Own Database** checkbox. Two additional fields appear:

       * **URL** — the connection URL for the Redis-compatible store. For example: `redis://:mypassword@redis.example.com:6379/0`
       * **Prefix** — a string prepended to every key when FastEdge reads or writes the store. For example, with prefix `app:prod:`, a read for key `greeting` retrieves `app:prod:greeting` from the external database. Use a prefix to namespace application data within a shared instance. Leave blank if no namespacing is needed.

    <Frame>
      <img src="https://mintcdn.com/gcore/ya83svzwxRUsGLmD/images/docs/fastedge/kv-stores/manage-kv-store/byod-create-storage-form.png?fit=max&auto=format&n=ya83svzwxRUsGLmD&q=85&s=a3797e06954773ee94a02b0eb362e6a5" alt="Add Edge Storage form with BYOD checkbox selected, URL and Prefix fields filled in" width="1400" height="625" data-path="images/docs/fastedge/kv-stores/manage-kv-store/byod-create-storage-form.png" />
    </Frame>

    5. Click **Create Edge Storage** to validate the connection and create the store.

    <Info>
      Gcore validates the connection to the external store when clicking **Create Edge Storage**. If the connection cannot be established — for example, because the host is unreachable, the port is blocked, or the credentials are wrong — the store is not created and an error message is displayed. Verify that the store is publicly accessible and the URL is correct, then try again.
    </Info>

    <Warning>
      A store cannot be converted between BYOD and hosted storage after creation. The store type is permanent.
    </Warning>

    ## Link a BYOD store to an application

    <p>After creation, link the BYOD store to a FastEdge application using the same process as a regular store — instructions are in [Managing applications](/fastedge/manage-apps#edge-storage). Once linked, the application accesses the BYOD store through the standard SDK API. The prefix is applied transparently and the application code does not need to know whether the store is BYOD or hosted.</p>
  </MethodSection>

  <MethodSection id="api" label="REST API">
    <p>Automate KV store management using the [Python SDK](/developer-tools/sdks/python) or [Go SDK](/developer-tools/sdks/go) for store-level operations, while key-value data operations require direct HTTP calls to the FastEdge REST API.</p>

    <Info>
      An [API token](/account-settings/api-tokens) is required. Retrieve the store ID from the response when creating a store or from the list endpoint.
    </Info>

    ```bash theme={null}
    export GCORE_API_KEY="{YOUR_API_KEY}"
    export STORE_ID="{YOUR_STORE_ID}"
    ```

    ## Create a store

    <p>A store must exist before key-value pairs can be written to it. The response includes the store ID used in all subsequent requests.</p>

    <Tabs>
      <Tab title="Python SDK">
        ```python theme={null}
        import os
        from gcore import Gcore

        client = Gcore(api_key=os.environ["GCORE_API_KEY"])

        kv_store = client.fastedge.kv_stores.create(
            name="my-store",
            comment="Optional description",
        )
        print(kv_store.name)
        ```
      </Tab>

      <Tab title="Go SDK">
        ```go theme={null}
        package main

        import (
            "context"
            "fmt"
            "os"

            "github.com/G-Core/gcore-go"
            "github.com/G-Core/gcore-go/fastedge"
            "github.com/G-Core/gcore-go/option"
        )

        func main() {
            client := gcore.NewClient(
                option.WithAPIKey(os.Getenv("GCORE_API_KEY")),
            )
            kvStore, err := client.Fastedge.KvStores.New(context.TODO(), fastedge.KvStoreNewParams{
                KvStore: fastedge.KvStoreParam{
                    Name:    "my-store",
                    Comment: "Optional description",
                },
            })
            if err != nil {
                panic(err.Error())
            }
            fmt.Printf("created store: %s\n", kvStore.Name)
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        curl -X POST "https://api.gcore.com/fastedge/v1/kv" \
          -H "Authorization: APIKey $GCORE_API_KEY" \
          -H "Content-Type: application/json" \
          -d '{"name": "my-store", "comment": "Optional description"}'
        ```

        <p>The API returns the new store ID:</p>

        ```json theme={null}
        {
          "id": 130,
          "name": "my-store",
          "comment": "Optional description",
          "app_count": 0,
          "size": 0
        }
        ```

        <p>Save the `id` value — it is required for all data operations.</p>
      </Tab>
    </Tabs>

    <p>To create a BYOD store backed by an external Redis-compatible database, add the `byod` object with the connection URL and an optional key prefix:</p>

    ```bash theme={null}
    curl -X POST "https://api.gcore.com/fastedge/v1/kv" \
      -H "Authorization: APIKey $GCORE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "my-byod-store",
        "byod": {
          "url": "redis://:mypassword@redis.example.com:6379/0",
          "prefix": "app:prod:"
        }
      }'
    ```

    <p>Gcore validates the connection at creation time and returns an error if the external store is unreachable. The store type cannot be changed after creation.</p>

    ## Insert or update key-value pairs

    <p>Use <code>PUT /fastedge/v1/kv/{store_id}/data</code> to write or overwrite key-value pairs. Each entry in the request array requires an `op` field — use `"add"` to insert or overwrite a key.</p>

    ```bash theme={null}
    curl -X PUT "https://api.gcore.com/fastedge/v1/kv/$STORE_ID/data" \
      -H "Authorization: APIKey $GCORE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '[
        {
          "op": "add",
          "key": "greeting",
          "datatype": "kv",
          "payload": {
            "encoding": "plain",
            "value": "Hello from FastEdge!"
          }
        },
        {
          "op": "add",
          "key": "color",
          "datatype": "kv",
          "payload": {
            "encoding": "plain",
            "value": "blue"
          }
        }
      ]'
    ```

    <p>The API returns write statistics:</p>

    ```json theme={null}
    {
      "write_count": 2,
      "del_count": 0,
      "write_size": 37,
      "store_size": 37,
      "revision": 577
    }
    ```

    <p>To store binary content, set `"encoding": "base64"` and provide the Base64-encoded value instead of plain text. The key length limit is 256 bytes.</p>

    ## List key-value pairs

    <p>Retrieve all pairs in a store with <code>GET /fastedge/v1/kv/{store_id}/data</code>. Use the `limit` and `offset` query parameters for pagination.</p>

    ```bash theme={null}
    curl "https://api.gcore.com/fastedge/v1/kv/$STORE_ID/data" \
      -H "Authorization: APIKey $GCORE_API_KEY"
    ```

    <p>The API returns:</p>

    ```json theme={null}
    {
      "count": 2,
      "entries": [
        {
          "key": "color",
          "datatype": "kv",
          "payload": { "encoding": "plain", "value": "blue" }
        },
        {
          "key": "greeting",
          "datatype": "kv",
          "payload": { "encoding": "plain", "value": "Hello from FastEdge!" }
        }
      ]
    }
    ```

    <p>To retrieve a single key, append the key name to the path: <code>GET /fastedge/v1/kv/{store_id}/data/{key}</code>.</p>

    ## Delete key-value pairs

    <p>Use the same <code>PUT /fastedge/v1/kv/{store_id}/data</code> endpoint with `"op": "del_key"` to delete one or more keys. Multiple keys can be deleted in a single request.</p>

    ```bash theme={null}
    curl -X PUT "https://api.gcore.com/fastedge/v1/kv/$STORE_ID/data" \
      -H "Authorization: APIKey $GCORE_API_KEY" \
      -H "Content-Type: application/json" \
      -d '[
        {"op": "del_key", "key": "color", "datatype": "kv"}
      ]'
    ```

    <p>The API returns:</p>

    ```json theme={null}
    {
      "del_count": 1,
      "write_count": 0,
      "write_size": 0,
      "store_size": 28,
      "revision": 578
    }
    ```

    ## Delete a store

    <p>Deleting a store permanently removes it and all its data. This action cannot be undone.</p>

    <Tabs>
      <Tab title="Python SDK">
        ```python theme={null}
        import os
        from gcore import Gcore

        client = Gcore(api_key=os.environ["GCORE_API_KEY"])

        store_id = 130  # replace with the actual store ID
        client.fastedge.kv_stores.delete(store_id)
        ```
      </Tab>

      <Tab title="Go SDK">
        ```go theme={null}
        import (
            "context"
            "os"

            "github.com/G-Core/gcore-go"
            "github.com/G-Core/gcore-go/option"
        )

        client := gcore.NewClient(option.WithAPIKey(os.Getenv("GCORE_API_KEY")))
        err := client.Fastedge.KvStores.Delete(context.TODO(), 130) // replace with actual store ID
        if err != nil {
            panic(err.Error())
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        curl -X DELETE "https://api.gcore.com/fastedge/v1/kv/$STORE_ID" \
          -H "Authorization: APIKey $GCORE_API_KEY"
        ```

        <p>Returns HTTP 204 with no body on success. Returns HTTP 409 if the store is still linked to one or more applications — unlink the store from all applications before deleting.</p>
      </Tab>
    </Tabs>
  </MethodSection>
</MethodSwitch>
