Skip to main content
Guyun
Language 简中 繁中 EN

Alibaba Cloud OSS

About 10 minutes and no cost: prepare a bucket on Alibaba Cloud and a pair of keys that cover that one bucket, so Guyun can use Alibaba Cloud OSS. This is the official Guyun guide; where the console has moved on, the console wins.

New to this? Start with an overseas region such as Hong Kong (cn-hongkong): no domain, ICP filing or certificate is involved. For mainland speed, read the two hard constraints first.

What you need

  • An Alibaba Cloud account. If you do not have one, register and verify your identity first (individuals can use an ID card).
  • A payment method on file. Activating OSS costs ¥0.00; you pay only for stored bytes, requests and public traffic.
  • A globally unique bucket name such as your-bucket-name: 3–63 lowercase letters, digits and hyphens, starting and ending with a letter or digit.
  • For a mainland region only: an ICP-filed domain, an HTTPS certificate, and a qualifying mainland server (Simple Application Server or ECS, prepaid for 3 months or more with public bandwidth) to claim a filing service code.
  • A computer, ideally: the RAM and AccessKey consoles are much easier to use on a desktop.

On an overseas region you need no domain, no filing, no certificate and no extra server.

Step by step

  1. Step 1

    Register an Alibaba Cloud account and verify your identity

    • Open the Alibaba Cloud sign-up page and register with a phone number or email address.
    • Complete identity verification — individuals can use an ID card. OSS can only be activated once it passes.
    • For a mainland region, keep the domain, the filing and OSS on the same account; that saves a step later.
  2. Step 2

    Activate Object Storage Service

    • Search the console for Object Storage Service and choose Activate.
    • The activation page lists a ¥0.00 order. Tick the agreement checkbox — you cannot continue without it — then continue to payment.
    • The amount due is ¥0.00; confirm it and open the console. Activation itself is free.
  3. Step 3

    Create a bucket

    • In the bucket list, choose Create bucket.
    • The name is globally unique, for example your-bucket-name: 3–63 lowercase letters, digits and hyphens, starting and ending with a letter or digit.
    • Pick the region closest to you and your users. Overseas, Hong Kong (cn-hongkong) is a good default; for a mainland region read the hard constraints below first.
    • Set access to private and leave block public access on.
    • Use the Standard storage class and leave versioning off — Guyun does not use it.
  4. Step 4

    Create a RAM user and get its AccessKey

    • Open Access Control (RAM) → Identities → Users and choose Create user.
    • Tick programmatic access (OpenAPI / permanent AccessKey); on newer consoles it reads “Access via permanent AccessKey”. Do not enable console logon.
    • The AccessKey ID and secret are shown once. Copy both immediately; the secret cannot be shown again.
    • Never use the root account key, and do not attach AliyunOSSFullAccess.
  5. Step 5

    Attach the least-privilege policy

    • Under Permissions → Policies, create a custom policy and switch to the script (JSON) editor.
    • Paste the policy below and replace your-bucket-name with your real bucket name.
    • Open the user again and attach the new custom policy to it.
    • Do not narrow the resource down to one folder. Guyun also writes the trash manifest, device records and cross-device handoff records under the .guyun/ prefix (.guyun/handoff/); narrowing it makes those writes fail.
    • Do not grant oss:*, bucket deletion, Bucket ACL changes or version management. Thumbnails are oss:GetObject and need nothing extra.
  6. Step 6

    Enter the four values in Guyun

    • Open Guyun's Add storage screen and choose Alibaba Cloud OSS.
    • Fill in the bucket name, the region (for example cn-hongkong), the endpoint (https://oss-{region}.aliyuncs.com) and the AccessKey ID and secret.
    • On an overseas region use the virtual host addressing mode; if you bound a custom domain, set the endpoint to that domain and choose custom domain (CNAME).
    • The key stays in this device's Keychain. It is never uploaded or synced — do not paste it into a chat, a document or a repository.
  7. Step 7

    Test the connection and save

    • Go back to Guyun and choose Test connection & save; it only calls ListObjectsV2 to list the root once.
    • Reading is not writing. Create a temporary folder in the bucket, then upload, download and delete one file before removing the folder.
    • If it fails, match the error against Common errors below.

Four routes

Pick the route that matches what you already have — the work differs a lot.

RouteWhat you needWhat it costs youWho it suits
Overseas region (e.g. Hong Kong cn-hongkong)Only OSS and a key pairCross-border access from the mainland is a little slowerRecommended for beginners — no ICP filing
Mainland region + custom domainAn ICP-filed domain, an HTTPS certificate, and a qualifying mainland server for the filing service code1–20 working days for the filing, plus a server you would not otherwise buyYou own a domain and want mainland speed
Mainland region + default domainOnly for accounts that activated OSS before 2025-03-20Unusable for new accounts; returns PublicEndpointForbiddenOlder accounts
Skip all this and use ROSOnly OSSTwo extra clicks (copy and paste the template)You want it done quickly

Least-privilege RAM policy

Paste this JSON into the custom policy you are creating. It grants read and write on this one bucket and nothing else, and no bucket administration.

json
{
  "Version": "1",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": ["oss:ListObjects", "oss:GetBucketLocation", "oss:ListMultipartUploads"],
      "Resource": ["acs:oss:*:*:your-bucket-name"]
    },
    {
      "Effect": "Allow",
      "Action": ["oss:GetObject", "oss:PutObject", "oss:DeleteObject", "oss:AbortMultipartUpload", "oss:ListParts"],
      "Resource": ["acs:oss:*:*:your-bucket-name/*"]
    }
  ]
}

Replace your-bucket-name with your real bucket name — missing this one replacement is the most common cause of a 403 for newcomers.

Three rules when attaching the policy

Do not narrow the resource range to one folder. Besides your own files, Guyun writes the trash manifest, device records and short-lived cross-device handoff records under the .guyun/ prefix (.guyun/handoff/). Narrowing it makes those writes fail.

Do not grant oss:* or AliyunOSSFullAccess, and do not allow bucket deletion, Bucket ACL changes or version management. Thumbnails are oss:GetObject; the policy above is enough.

Do not use the root account key. Create a separate RAM user for Guyun and scope it to this one bucket.

The two hard constraints in a mainland region

1. Filing service code: OSS does not provide one. An ICP filing for an OSS domain needs a filing service code, and only qualifying mainland server products hand those out (Simple Application Server or ECS, mainland node, prepaid for 3 months or more with public bandwidth). OSS is not on that list, and trial or pay-as-you-go ECS does not qualify. If the domain is registered with an overseas registrar (GoDaddy, Namecheap, Cloudflare …), transfer it to a mainland registrar first.

2. PublicEndpointForbidden: the default domain is refused. For accounts that activated OSS on or after 2025-03-20, accessing a mainland bucket's data APIs (uploads and downloads included) through a default public endpoint such as oss-cn-hangzhou.aliyuncs.com is rejected. It limits the domain, not the permissions: a private bucket with public access blocked and no shared links is still refused. The test is when OSS was activated, not when the bucket was created, and the S3-compatible endpoint s3.oss-{region}.aliyuncs.com is refused too.

To stay in a mainland region you must bind an ICP-filed custom domain and serve it over HTTPS. Otherwise move the bucket to Hong Kong or another overseas region, where the default endpoint is not restricted.

One-shot route with ROS

If you would rather not click through every step, let Resource Orchestration Service (ROS) build it from the template below in one go: a private bucket with public access blocked, a RAM user limited to that bucket, and that user's AccessKey. Copy the AccessKey ID and secret from the stack outputs when it finishes.

  • Open the ROS console at https://ros.console.aliyun.com/. On an international-site account use ros.console.alibabacloud.com.
  • Copy the whole template below, choose Specify template → Enter template on the create page, set the format to ROS + JSON, paste it in and continue.
  • Pitfall 1: dependencies must be explicit. Where the template names the RAM user for the policy and the AccessKey, taking that name from a parameter with Ref does not make ROS create a dependency; the template must say DependsOn: AppUser explicitly. This one does, and it references the resource attribute with Fn::GetAtt. Drop either and creation fails with a validation error such as The user … is not exists.
  • Pitfall 2: RAM's DisplayName is capped at 12 characters (the documentation says 1–128; the API allows 12, with a Chinese character counting as one). Anything longer is rejected during validation. This template uses the fixed value 孤云存储 and keeps the bucket name in the comment field.
  • Pitfall 3: there is a safety confirmation checkbox at the bottom of the create page (“I confirm that Alibaba Cloud ROS may create RAM resources”). Creation is blocked until you tick it. Leave the stack name at its generated default (something like guyun_2026-09-11_xxxx).
  • When it fails, open the stack's Events: a taken bucket name is the most common cause; The Parameter (BucketName) was not defined in template means the template was not pasted (go back to step 1); a missing safety confirmation also blocks creation.
  • When you build a second bucket in the same account, change the RAM user name on the create page from the default guyun-app to something else (for example guyun-{bucket}); creation fails if that user already exists.
  • Deleting the stack deletes the bucket and the RAM user with it, so do not delete it once the bucket holds data.

ROS template (verbatim)

Copy the whole thing and paste it into the Enter template box on the ROS create page.

json
{
  "ROSTemplateFormatVersion": "2015-09-01",
  "Description": {
    "zh-cn": "为孤云创建:私有且阻止公共访问的 OSS Bucket、只授权该桶读写的 RAM 用户、以及该用户的 AccessKey。AccessKey ID 与 Secret 会出现在资源栈的「输出」里,复制回应用即可。",
    "en": "Creates, for Guyun: a private OSS bucket with public access blocked, a RAM user limited to that bucket, and an AccessKey for it. The AccessKey ID and secret appear in the stack outputs; copy them back into the app."
  },
  "Parameters": {
    "BucketName": {
      "Type": "String",
      "Label": {
        "zh-cn": "Bucket 名称",
        "en": "Bucket name"
      },
      "Description": {
        "zh-cn": "全局唯一。3–63 位,只能用小写字母、数字和短横线,首尾必须是字母或数字。例如 my-photos-2026。",
        "en": "Globally unique. 3–63 characters: lowercase letters, digits and hyphens, starting and ending with a letter or digit. For example my-photos-2026."
      },
      "AllowedPattern": "^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$",
      "MinLength": 3,
      "MaxLength": 63
    },
    "RamUserName": {
      "Type": "String",
      "Label": {
        "zh-cn": "RAM 用户名",
        "en": "RAM user name"
      },
      "Description": {
        "zh-cn": "给应用专用的子账号。如果账号下已经有同名用户,请改成别的名字再创建。",
        "en": "A dedicated sub-account for the app. If a user with this name already exists, pick another one."
      },
      "Default": "guyun-app",
      "AllowedPattern": "^[a-zA-Z0-9._-]{1,64}$",
      "MinLength": 1,
      "MaxLength": 64
    }
  },
  "Resources": {
    "Bucket": {
      "Type": "ALIYUN::OSS::Bucket",
      "Properties": {
        "BucketName": {
          "Ref": "BucketName"
        },
        "AccessControl": "private",
        "StorageClass": "Standard",
        "BlockPublicAccess": true
      }
    },
    "AppUser": {
      "Type": "ALIYUN::RAM::User",
      "Properties": {
        "UserName": {
          "Ref": "RamUserName"
        },
        "DisplayName": {
          "Fn::Sub": "孤云存储"
        },
        "Comments": {
          "Fn::Sub": "孤云专用存储用户;授权桶 ${BucketName} 的读写;随资源栈删除。"
        }
      }
    },
    "BucketPolicy": {
      "Type": "ALIYUN::RAM::ManagedPolicy",
      "DependsOn": "AppUser",
      "Properties": {
        "PolicyName": {
          "Fn::Sub": "guyun-${BucketName}-rw"
        },
        "Description": {
          "Fn::Sub": "Guyun: read and write ${BucketName} only. No bucket-level administration."
        },
        "PolicyDocument": {
          "Version": "1",
          "Statement": [
            {
              "Effect": "Allow",
              "Action": [
                "oss:ListObjects",
                "oss:GetBucketLocation",
                "oss:ListMultipartUploads"
              ],
              "Resource": [
                {
                  "Fn::Sub": "acs:oss:*:*:${BucketName}"
                }
              ]
            },
            {
              "Effect": "Allow",
              "Action": [
                "oss:GetObject",
                "oss:PutObject",
                "oss:DeleteObject",
                "oss:AbortMultipartUpload",
                "oss:ListParts"
              ],
              "Resource": [
                {
                  "Fn::Sub": "acs:oss:*:*:${BucketName}/*"
                }
              ]
            }
          ]
        },
        "Users": [
          {
            "Fn::GetAtt": [
              "AppUser",
              "UserName"
            ]
          }
        ]
      }
    },
    "AccessKey": {
      "Type": "ALIYUN::RAM::AccessKey",
      "DependsOn": "AppUser",
      "Properties": {
        "UserName": {
          "Fn::GetAtt": [
            "AppUser",
            "UserName"
          ]
        }
      }
    }
  },
  "Outputs": {
    "BucketName": {
      "Label": {
        "zh-cn": "Bucket 名称",
        "en": "Bucket name"
      },
      "Description": {
        "zh-cn": "填到应用的「Bucket 名称」里。",
        "en": "Paste into the app's bucket name field."
      },
      "Value": {
        "Ref": "BucketName"
      }
    },
    "RegionId": {
      "Label": {
        "zh-cn": "地域",
        "en": "Region"
      },
      "Description": {
        "zh-cn": "资源栈所在地域,也就是 Bucket 的地域。",
        "en": "The stack's region, which is the bucket's region."
      },
      "Value": {
        "Ref": "ALIYUN::Region"
      }
    },
    "Endpoint": {
      "Label": {
        "zh-cn": "HTTPS Endpoint",
        "en": "HTTPS endpoint"
      },
      "Description": {
        "zh-cn": "走默认域名时填到应用的「HTTPS Endpoint」里(地址方式选「Bucket 子域名」);如果之后绑了自定义域名,Endpoint 要改成那个域名。",
        "en": "With the default endpoint, paste this into the app's HTTPS endpoint field (addressing: virtual host). If you bind a custom domain later, the endpoint becomes that domain instead."
      },
      "Value": {
        "Fn::Sub": "https://oss-${ALIYUN::Region}.aliyuncs.com"
      }
    },
    "AccessKeyId": {
      "Label": {
        "zh-cn": "AccessKey ID",
        "en": "AccessKey ID"
      },
      "Description": {
        "zh-cn": "复制后填到应用的「AccessKey ID」里。",
        "en": "Copy into the app's AccessKey ID field."
      },
      "Value": {
        "Fn::GetAtt": [
          "AccessKey",
          "AccessKeyId"
        ]
      }
    },
    "AccessKeySecret": {
      "Label": {
        "zh-cn": "AccessKey Secret",
        "en": "AccessKey secret"
      },
      "Description": {
        "zh-cn": "只在这里显示,复制后填进应用的 Keychain;不要粘贴到聊天、文档或代码里。",
        "en": "Shown here only. Copy it into the app's Keychain; never paste it into a chat, document or repository."
      },
      "Value": {
        "Fn::GetAtt": [
          "AccessKey",
          "AccessKeySecret"
        ]
      }
    }
  },
  "Metadata": {
    "ALIYUN::ROS::Interface": {
      "ParameterGroups": [
        {
          "Parameters": [
            "BucketName"
          ],
          "Label": {
            "default": "OSS"
          }
        },
        {
          "Parameters": [
            "RamUserName"
          ],
          "Label": {
            "default": "RAM"
          }
        }
      ],
      "Outputs": [
        "BucketName",
        "RegionId",
        "Endpoint",
        "AccessKeyId",
        "AccessKeySecret"
      ]
    }
  }
}

This template produces exactly the same resources and permissions as doing it by hand: a private bucket with public access blocked, a policy limited to that bucket, and an AccessKey for that user. The text is right here on the page, so nothing has to be trusted from a third-party host.

The four values to copy

These four go into Guyun's Add storage screen:

This valueWhere it goes in Guyun
Bucket name
your-bucket-name (your real bucket)
Bucket name
Region
cn-hongkong
Region
Endpoint
https://oss-cn-hongkong.aliyuncs.com
Endpoint
Credentials
the RAM user's AccessKey ID and secret
AccessKey ID and secret

How to tell it worked

  • Guyun's test connection succeeds and lists the bucket root — an empty bucket returning an empty list counts as success.
  • For a full check, create a temporary folder, then upload a file, download it and delete it. All three passing means read and write are both in place.
  • None of the common errors appear: no 403, no PublicEndpointForbidden, no SignatureDoesNotMatch.

Go back to Guyun and choose Test connection & save.

Common errors

Match the code from the test connection against this table.

Symptom / codeWhat it meansWhat to do
403 AccessDeniedThe key lacks permission, or the bucket name in the custom policy was never replaced; the policy may also not be attached to this userCheck step 5: that your-bucket-name was replaced, that the policy is attached to this user, and that the key belongs to that user
PublicEndpointForbiddenMainland region, default public endpoint, and OSS activated on or after 2025-03-20Bind an ICP-filed custom domain (the endpoint becomes that domain and the addressing mode becomes CNAME), or move the bucket to Hong Kong or another overseas region
SignatureDoesNotMatch / InvalidAccessKeyIdThe key was copied wrong, or the secret was never saved when it was shown once; the ID and secret may also come from two different pairsCreate a fresh AccessKey on the RAM user and copy both values into Guyun together
Connection test passes but uploads failThe test only called ListObjectsV2 — reading is not writingCheck that the policy grants oss:PutObject / oss:DeleteObject, then upload, download and delete one file in a temporary folder

If the code is not in the table, keep the full error text and the request ID — support tickets need them.