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

# Managing buckets

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">
    <p>Gcore Object Storage organizes files into buckets. The built-in file manager handles uploading and organizing files, with controls for public access and lifecycle policies.</p>

    ## Bucket creation

    <Steps>
      <Step title="Open the bucket list">
        Navigate to the [Gcore Customer Portal](https://portal.gcore.com) > **Storage** > **Object Storage** and click the relevant storage name. Alternatively, open the storage's three-dot menu and select **Buckets**.

        <Frame>
          <img src="https://mintcdn.com/gcore/bP_M3iNvkCNJWoX1/images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/manage-buckets-cp-10.png?fit=max&auto=format&n=bP_M3iNvkCNJWoX1&q=85&s=8161333914808c42d551d0b0f589a5ae" alt="Object Storage list with storage rows" width="1369" height="591" data-path="images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/manage-buckets-cp-10.png" />
        </Frame>
      </Step>

      <Step title="Click Add new bucket">
        <Frame>
          <img src="https://mintcdn.com/gcore/bP_M3iNvkCNJWoX1/images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/manage-buckets-cp-20.png?fit=max&auto=format&n=bP_M3iNvkCNJWoX1&q=85&s=d259c3396709882c4be00f85289c2575" alt="Bucket list with Add new bucket button" width="1368" height="386" data-path="images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/manage-buckets-cp-20.png" />
        </Frame>
      </Step>

      <Step title="Name the bucket">
        In the dialog that appears, enter a name that meets the following criteria:

        * 3–63 characters.
        * Lowercase only.
        * No underscores, trailing dashes, consecutive dots, or a mix of dots and dashes — these conflict with DNS notation rules.
        * Unique across the entire Gcore Object Storage system. A `This bucket name already exists. Please use a different name` error appears if the name is taken.
      </Step>

      <Step title="Click Create" />
    </Steps>

    <Note>Avoid using sensitive information in the bucket name. The bucket name appears in object URLs and is publicly visible.</Note>

    <p>The new bucket appears in the list once created. The following sections cover how to upload and manage files, control public access, and configure lifecycle policies.</p>

    ## File manager

    <p>The file manager is a browser-based interface for managing files in a bucket. Two setup steps are required before use: configuring CORS and authenticating with S3 credentials.</p>

    ### CORS setup

    <p>To access the file manager, add `https://portal.gcore.com` to the bucket's CORS policy. This is a one-time setup per bucket.</p>

    <p>Click the bucket name to open the access settings dialog, then click **Override CORS**.</p>

    <Note>Configure CORS individually for each bucket.</Note>

    <Frame>
      <img src="https://mintcdn.com/gcore/bP_M3iNvkCNJWoX1/images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/manage-buckets-cp-30.png?fit=max&auto=format&n=bP_M3iNvkCNJWoX1&q=85&s=77d690b9d5a308d26fe140337febdcec" alt="Override CORS dialog" width="745" height="652" data-path="images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/manage-buckets-cp-30.png" />
    </Frame>

    <p>The [API](/api-reference/storage/storage/create-s3-bucket-cors) also supports multiple allowed origins beyond `https://portal.gcore.com`.</p>

    ### Authorization

    <p>To authorize, click the bucket name, enter the Access key and Secret key, then click **Auth**.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/bP_M3iNvkCNJWoX1/images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/manage-buckets-cp-40.png?fit=max&auto=format&n=bP_M3iNvkCNJWoX1&q=85&s=8b8cd891092b22806544de89013b19e1" alt="Authorization form for the file manager" width="809" height="665" data-path="images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/manage-buckets-cp-40.png" />
    </Frame>

    <p>Alternatively, access the file manager from the bucket's three-dot menu by selecting **File manager**.</p>

    <Accordion title="Access key and Secret key features">
      <Note>Keys entered during a session remain active for that session. Each new session requires entering them again.</Note>

      <p>To avoid re-entering keys manually, save them using the browser's credential manager.</p>

      <p>Storage creation generates the Access key and Secret key. Gcore does not store these values. To regenerate lost keys, click **Generate new keys** in the **Object Storage** list using the three-dot menu.</p>

      <Frame>
        <img src="https://mintcdn.com/gcore/bP_M3iNvkCNJWoX1/images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/manage-buckets-cp-60.png?fit=max&auto=format&n=bP_M3iNvkCNJWoX1&q=85&s=d453ba73b93d7fc0b43efdf930e0c059" alt="Object storage three-dot menu with the Generate new keys option" width="1104" height="592" data-path="images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/manage-buckets-cp-60.png" />
      </Frame>
    </Accordion>

    ### Adding folders

    <p>After [authorizing](/storage/manage-object-storage/manage-buckets-via-the-control-panel#authorization), click **Add folder**. Enter a name in the field and click **Create**.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/bP_M3iNvkCNJWoX1/images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/manage-buckets-cp-70.png?fit=max&auto=format&n=bP_M3iNvkCNJWoX1&q=85&s=3b0eb9752964870874d22597dc814407" alt="Add folder dialog" width="776" height="604" data-path="images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/manage-buckets-cp-70.png" />
    </Frame>

    <p>Each folder displays its last modification date. Click a folder name to open it.</p>

    ### Uploading files

    <p>To upload files, [authorize](/storage/manage-object-storage/manage-buckets-via-the-control-panel#authorization) and click **Select and upload file(s)** either in the bucket root or within a specific folder. Then, follow the standard upload process.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/bP_M3iNvkCNJWoX1/images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/manage-buckets-cp-80.png?fit=max&auto=format&n=bP_M3iNvkCNJWoX1&q=85&s=c49b5ffdebe375427fc2c85ef3214980" alt="File manager with uploaded file and folder" width="1367" height="433" data-path="images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/manage-buckets-cp-80.png" />
    </Frame>

    ### Deleting folders and files

    <p>To delete folders or files, [authorize](/storage/manage-object-storage/manage-buckets-via-the-control-panel#authorization) and click **Delete** next to the desired object. Alternatively, select the checkboxes next to the object names and click **Delete selected**.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/bP_M3iNvkCNJWoX1/images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/manage-buckets-cp-90.png?fit=max&auto=format&n=bP_M3iNvkCNJWoX1&q=85&s=81f50da8bbff4f789bef77c82995ebad" alt="Delete confirmation dialog" width="763" height="603" data-path="images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/manage-buckets-cp-90.png" />
    </Frame>

    <Note>Deleting a folder removes all files nested within it.</Note>

    ### Copying file URLs

    <p>To copy file URLs, [authorize](/storage/manage-object-storage/manage-buckets-via-the-control-panel#authorization), select objects, and click the relevant button: **Copy S3 URL** or **Copy URL**.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/bP_M3iNvkCNJWoX1/images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/manage-buckets-cp-100.png?fit=max&auto=format&n=bP_M3iNvkCNJWoX1&q=85&s=1e8a084f98ad18b43f53beeb75e6541b" alt="Copy S3 URL and Copy URL buttons visible for a selected file" width="1440" height="501" data-path="images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/manage-buckets-cp-100.png" />
    </Frame>

    <p>The links for the file `image 3972.png` look like this:</p>

    * `s3://s-ed1.cloud.gcore.lu/000-sample-for-articles/test/image%203972.png`
    * `https://s-ed1.cloud.gcore.lu/000-sample-for-articles/test/image%203972.png`

    <Note>Without public HTTP access enabled, the direct file URL returns a 403 error.</Note>

    ## Managing access

    <p>Enable public HTTP access to make files directly accessible by URL or to serve them through a [CDN origin](/storage/use-storage-as-the-origin-for-your-cdn-resource). Click the three-dot menu on the bucket row, select **Public access to all files**, and confirm by clicking **Apply**.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/bP_M3iNvkCNJWoX1/images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/http-access-s3-bucket.png?fit=max&auto=format&n=bP_M3iNvkCNJWoX1&q=85&s=d52ea598879587fd46ab6abfe9800a99" alt="Confirmation dialog for enabling public HTTP access to all files in the bucket" width="764" height="373" data-path="images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/http-access-s3-bucket.png" />
    </Frame>

    <p>To revert to private access, click the three-dot menu on the bucket row and click **Default access to all files**.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/bP_M3iNvkCNJWoX1/images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/default-access-s3-bucket.png?fit=max&auto=format&n=bP_M3iNvkCNJWoX1&q=85&s=ee4beee77cbb064a7fdec0c9af5052bb" alt="Confirmation dialog for reverting the bucket to default private access" width="706" height="409" data-path="images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/default-access-s3-bucket.png" />
    </Frame>

    ## Adding lifecycle policy (for S3 in Luxembourg only)

    <p>Click the three-dot menu on the bucket row and select **Lifecycle Management**. Set the retention period and click **Save changes**.</p>

    <Frame>
      <img src="https://mintcdn.com/gcore/bP_M3iNvkCNJWoX1/images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/manage-buckets-cp-120.png?fit=max&auto=format&n=bP_M3iNvkCNJWoX1&q=85&s=dcff4b0eab047cd55328ff5888818765" alt="Lifecycle policy configuration dialog" width="762" height="743" data-path="images/docs/storage/manage-object-storage/manage-buckets-via-the-control-panel/manage-buckets-cp-120.png" />
    </Frame>

    <p>To remove an existing policy, click **Cancel policy**.</p>

    <p>For other storage locations, configure lifecycle policies with the [AWS CLI](/storage/manage-object-storage/configure-aws-sli-s3cmd-and-aws-javascript-sdk/remove-objects-from-a-bucket-automatically-with-aws-cli).</p>
  </MethodSection>

  <MethodSection id="api" label="REST API">
    <p>Bucket operations within an S3 storage — creation, CORS rules, public access, and lifecycle policies — are managed through the buckets endpoint. Each request requires the numeric storage ID, returned when [creating storage](/storage/create-an-s3-or-sftp-storage) or available from the [storage list](/api-reference/storage/storage/list-object-storages).</p>

    <Info>
      An [API token](/account-settings/api-tokens) is required.
    </Info>

    ```bash theme={null}
    export GCORE_API_KEY="{YOUR_API_KEY}"
    export STORAGE_ID="{YOUR_STORAGE_ID}"
    ```

    ## Create bucket

    <p>Bucket names must be globally unique across the Gcore Object Storage system, 3–63 lowercase characters, with no underscores, trailing dashes, consecutive dots, or combinations of dots and dashes.</p>

    | Parameter | Required | Description                                            |
    | --------- | -------- | ------------------------------------------------------ |
    | `name`    | Yes      | Globally unique bucket name, 3–63 lowercase characters |

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

        client = Gcore()
        storage_id = int(os.environ["STORAGE_ID"])

        bucket = client.storage.object_storages.buckets.create(
            storage_id,
            name="my-bucket",
        )
        print(f"Bucket: {bucket.name}  storage_id={bucket.storage_id}")
        ```
      </Tab>

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

        import (
            "context"
            "fmt"
            "log"
            "os"
            "strconv"

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

        func main() {
            storageID, _ := strconv.ParseInt(os.Getenv("STORAGE_ID"), 10, 64)

            client := gcore.NewClient()
            ctx := context.Background()

            bucket, err := client.Storage.ObjectStorages.Buckets.New(ctx, storageID, storage.ObjectStorageBucketNewParams{
                Name: "my-bucket",
            })
            if err != nil {
                log.Fatalf("create bucket: %v", err)
            }
            fmt.Printf("Bucket: %s  storage_id=%d\n", bucket.Name, bucket.StorageID)
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        curl -X POST "https://api.gcore.com/storage/v4/object_storages/$STORAGE_ID/buckets" \
          -H "Authorization: APIKey $GCORE_API_KEY" \
          -H "Content-Type: application/json" \
          -d '{"name": "my-bucket"}'
        ```

        The API returns:

        ```json theme={null}
        {
          "name": "my-bucket",
          "storage_id": 13799,
          "cors": null,
          "lifecycle": null,
          "policy": null
        }
        ```
      </Tab>
    </Tabs>

    ## List buckets

    <p>Returns all buckets in a storage, including their current CORS, lifecycle, and public access settings.</p>

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

        client = Gcore()
        storage_id = int(os.environ["STORAGE_ID"])

        buckets = client.storage.object_storages.buckets.list(storage_id)
        for b in buckets:
            print(f"{b.name}  is_public={b.policy.is_public if b.policy else False}")
        ```
      </Tab>

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

        import (
            "context"
            "fmt"
            "log"
            "os"
            "strconv"

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

        func main() {
            storageID, _ := strconv.ParseInt(os.Getenv("STORAGE_ID"), 10, 64)

            client := gcore.NewClient()
            ctx := context.Background()

            page, err := client.Storage.ObjectStorages.Buckets.List(ctx, storageID, storage.ObjectStorageBucketListParams{})
            if err != nil {
                log.Fatalf("list buckets: %v", err)
            }
            for _, b := range page.Results {
                fmt.Printf("%s  is_public=%v\n", b.Name, b.Policy.IsPublic)
            }
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        curl "https://api.gcore.com/storage/v4/object_storages/$STORAGE_ID/buckets" \
          -H "Authorization: APIKey $GCORE_API_KEY"
        ```

        The API returns:

        ```json theme={null}
        {
          "count": 2,
          "results": [
            {
              "name": "my-bucket",
              "storage_id": 13799,
              "cors": {
                "allowed_origins": ["https://portal.gcore.com"]
              },
              "lifecycle": null,
              "policy": {
                "is_public": false
              }
            },
            {
              "name": "my-other-bucket",
              "storage_id": 13799,
              "cors": null,
              "lifecycle": {
                "expiration_days": 30
              },
              "policy": {
                "is_public": true
              }
            }
          ]
        }
        ```
      </Tab>
    </Tabs>

    ## Configure CORS

    <p>CORS rules control which web origins can make direct browser requests to the bucket. Sending an empty array removes all CORS rules.</p>

    | Parameter              | Required | Description                                                                                 |
    | ---------------------- | -------- | ------------------------------------------------------------------------------------------- |
    | `cors.allowed_origins` | Yes      | List of allowed origin URLs, e.g. `["https://portal.gcore.com"]`. Send `[]` to remove CORS. |

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

        client = Gcore()
        storage_id = int(os.environ["STORAGE_ID"])

        bucket = client.storage.object_storages.buckets.update(
            "my-bucket",
            storage_id=storage_id,
            cors={"allowed_origins": ["https://portal.gcore.com", "https://example.com"]},
        )
        print(f"Allowed origins: {bucket.cors.allowed_origins}")
        ```
      </Tab>

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

        import (
            "context"
            "fmt"
            "log"
            "os"
            "strconv"

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

        func main() {
            storageID, _ := strconv.ParseInt(os.Getenv("STORAGE_ID"), 10, 64)

            client := gcore.NewClient()
            ctx := context.Background()

            bucket, err := client.Storage.ObjectStorages.Buckets.Update(ctx, "my-bucket", storage.ObjectStorageBucketUpdateParams{
                StorageID: storageID,
                Cors: storage.ObjectStorageBucketUpdateParamsCors{
                    AllowedOrigins: []string{"https://portal.gcore.com", "https://example.com"},
                },
            })
            if err != nil {
                log.Fatalf("update CORS: %v", err)
            }
            fmt.Printf("Allowed origins: %v\n", bucket.Cors.AllowedOrigins)
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        curl -X PATCH "https://api.gcore.com/storage/v4/object_storages/$STORAGE_ID/buckets/my-bucket" \
          -H "Authorization: APIKey $GCORE_API_KEY" \
          -H "Content-Type: application/json" \
          -d '{"cors": {"allowed_origins": ["https://portal.gcore.com", "https://example.com"]}}'
        ```

        The API returns the updated bucket object with `cors.allowed_origins` set.
      </Tab>
    </Tabs>

    ## Enable or disable public access

    <p>Setting `is_public` to `true` allows unauthenticated downloads of all objects in the bucket by direct URL. Set to `false` to require S3 credentials for all requests.</p>

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

        client = Gcore()
        storage_id = int(os.environ["STORAGE_ID"])

        # Enable public access
        bucket = client.storage.object_storages.buckets.update(
            "my-bucket",
            storage_id=storage_id,
            policy={"is_public": True},
        )
        print(f"is_public={bucket.policy.is_public}")

        # Revert to private
        bucket = client.storage.object_storages.buckets.update(
            "my-bucket",
            storage_id=storage_id,
            policy={"is_public": False},
        )
        print(f"is_public={bucket.policy.is_public}")
        ```
      </Tab>

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

        import (
            "context"
            "fmt"
            "log"
            "os"
            "strconv"

            gcore "github.com/G-Core/gcore-go"
            "github.com/G-Core/gcore-go/packages/param"
            "github.com/G-Core/gcore-go/storage"
        )

        func main() {
            storageID, _ := strconv.ParseInt(os.Getenv("STORAGE_ID"), 10, 64)

            client := gcore.NewClient()
            ctx := context.Background()

            // Enable public access
            bucket, err := client.Storage.ObjectStorages.Buckets.Update(ctx, "my-bucket", storage.ObjectStorageBucketUpdateParams{
                StorageID: storageID,
                Policy: storage.ObjectStorageBucketUpdateParamsPolicy{
                    IsPublic: param.NewOpt[bool](true),
                },
            })
            if err != nil {
                log.Fatalf("enable public access: %v", err)
            }
            fmt.Printf("is_public=%v\n", bucket.Policy.IsPublic)
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        # Enable public access
        curl -X PATCH "https://api.gcore.com/storage/v4/object_storages/$STORAGE_ID/buckets/my-bucket" \
          -H "Authorization: APIKey $GCORE_API_KEY" \
          -H "Content-Type: application/json" \
          -d '{"policy": {"is_public": true}}'

        # Revert to private
        curl -X PATCH "https://api.gcore.com/storage/v4/object_storages/$STORAGE_ID/buckets/my-bucket" \
          -H "Authorization: APIKey $GCORE_API_KEY" \
          -H "Content-Type: application/json" \
          -d '{"policy": {"is_public": false}}'
        ```

        The API returns the updated bucket object with `policy.is_public` reflecting the new value.
      </Tab>
    </Tabs>

    ## Set lifecycle policy

    <p>Lifecycle policies automatically delete objects after a set number of days. This feature is available for S3 storage in Luxembourg only. Set `expiration_days` to a positive integer to enable, or to `0` to remove an existing policy.</p>

    | Parameter                   | Required | Description                                                  |
    | --------------------------- | -------- | ------------------------------------------------------------ |
    | `lifecycle.expiration_days` | Yes      | Days before objects are deleted. Use `0` to remove the rule. |

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

        client = Gcore()
        storage_id = int(os.environ["STORAGE_ID"])

        # Set 30-day expiration
        bucket = client.storage.object_storages.buckets.update(
            "my-bucket",
            storage_id=storage_id,
            lifecycle={"expiration_days": 30},
        )
        print(f"expiration_days={bucket.lifecycle.expiration_days}")

        # Remove the lifecycle policy
        bucket = client.storage.object_storages.buckets.update(
            "my-bucket",
            storage_id=storage_id,
            lifecycle={"expiration_days": 0},
        )
        print(f"lifecycle={bucket.lifecycle}")  # None
        ```
      </Tab>

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

        import (
            "context"
            "fmt"
            "log"
            "os"
            "strconv"

            gcore "github.com/G-Core/gcore-go"
            "github.com/G-Core/gcore-go/packages/param"
            "github.com/G-Core/gcore-go/storage"
        )

        func main() {
            storageID, _ := strconv.ParseInt(os.Getenv("STORAGE_ID"), 10, 64)

            client := gcore.NewClient()
            ctx := context.Background()

            // Set 30-day expiration
            bucket, err := client.Storage.ObjectStorages.Buckets.Update(ctx, "my-bucket", storage.ObjectStorageBucketUpdateParams{
                StorageID: storageID,
                Lifecycle: storage.ObjectStorageBucketUpdateParamsLifecycle{
                    ExpirationDays: param.NewOpt[int64](30),
                },
            })
            if err != nil {
                log.Fatalf("set lifecycle: %v", err)
            }
            fmt.Printf("expiration_days=%d\n", bucket.Lifecycle.ExpirationDays)

            // Remove the lifecycle policy
            bucket, err = client.Storage.ObjectStorages.Buckets.Update(ctx, "my-bucket", storage.ObjectStorageBucketUpdateParams{
                StorageID: storageID,
                Lifecycle: storage.ObjectStorageBucketUpdateParamsLifecycle{
                    ExpirationDays: param.NewOpt[int64](0),
                },
            })
            if err != nil {
                log.Fatalf("remove lifecycle: %v", err)
            }
            fmt.Printf("lifecycle removed: expiration_days=%d\n", bucket.Lifecycle.ExpirationDays)
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        # Set 30-day expiration
        curl -X PATCH "https://api.gcore.com/storage/v4/object_storages/$STORAGE_ID/buckets/my-bucket" \
          -H "Authorization: APIKey $GCORE_API_KEY" \
          -H "Content-Type: application/json" \
          -d '{"lifecycle": {"expiration_days": 30}}'

        # Remove the lifecycle policy
        curl -X PATCH "https://api.gcore.com/storage/v4/object_storages/$STORAGE_ID/buckets/my-bucket" \
          -H "Authorization: APIKey $GCORE_API_KEY" \
          -H "Content-Type: application/json" \
          -d '{"lifecycle": {"expiration_days": 0}}'
        ```

        The API returns the updated bucket object. When the policy is removed, `lifecycle` is `null`.
      </Tab>
    </Tabs>

    ## Delete bucket

    <p>Deleting a bucket permanently removes it and all objects it contains. The bucket name becomes available again after deletion.</p>

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

        client = Gcore()
        storage_id = int(os.environ["STORAGE_ID"])

        client.storage.object_storages.buckets.delete("my-bucket", storage_id=storage_id)
        print("Bucket deleted")
        ```
      </Tab>

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

        import (
            "context"
            "fmt"
            "log"
            "os"
            "strconv"

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

        func main() {
            storageID, _ := strconv.ParseInt(os.Getenv("STORAGE_ID"), 10, 64)

            client := gcore.NewClient()
            ctx := context.Background()

            err := client.Storage.ObjectStorages.Buckets.Delete(ctx, "my-bucket", storage.ObjectStorageBucketDeleteParams{
                StorageID: storageID,
            })
            if err != nil {
                log.Fatalf("delete bucket: %v", err)
            }
            fmt.Println("Bucket deleted")
        }
        ```
      </Tab>

      <Tab title="curl">
        ```bash theme={null}
        curl -X DELETE "https://api.gcore.com/storage/v4/object_storages/$STORAGE_ID/buckets/my-bucket" \
          -H "Authorization: APIKey $GCORE_API_KEY"
        ```

        Returns HTTP 204 with no response body.
      </Tab>
    </Tabs>
  </MethodSection>

  <MethodSection id="terraform" label="Terraform">
    <p>Declare S3 buckets with CORS rules, public access policy, and lifecycle settings using the Gcore [Terraform provider](/developer-tools/terraform/overview) v2.</p>

    ## Create bucket

    <p>Declares a bucket in an existing S3 storage. CORS rules, public access, and lifecycle policy are optional — omit any block to leave that setting unconfigured.</p>

    ```hcl theme={null}
    resource "gcore_storage_object_storage_bucket" "example" {
      storage_id = var.storage_id
      name       = "my-bucket"

      cors = {
        allowed_origins = ["https://portal.gcore.com"]
      }

      policy = {
        is_public = false
      }

      storage_object_storage_bucket_lifecycle = {
        expiration_days = 30
      }
    }

    variable "storage_id" {
      type = number
    }
    ```

    ```bash theme={null}
    terraform apply
    ```

    ## Update bucket settings

    <p>Edit any field in the resource block and run `terraform apply`. To remove the lifecycle policy, omit the `storage_object_storage_bucket_lifecycle` attribute.</p>

    ```hcl theme={null}
    resource "gcore_storage_object_storage_bucket" "example" {
      storage_id = var.storage_id
      name       = "my-bucket"

      cors = {
        allowed_origins = ["https://portal.gcore.com", "https://example.com"]
      }

      policy = {
        is_public = true
      }

      # Omit storage_object_storage_bucket_lifecycle to remove the lifecycle policy
    }
    ```

    ```bash theme={null}
    terraform apply
    ```

    ## Delete bucket

    <p>Remove the resource block — Terraform deletes the bucket and all its objects on the next apply.</p>

    ```hcl theme={null}
    # Remove or comment out this block:
    # resource "gcore_storage_object_storage_bucket" "example" {
    #   storage_id = var.storage_id
    #   name       = "my-bucket"
    # }
    ```

    ```bash theme={null}
    terraform apply
    ```
  </MethodSection>
</MethodSwitch>
