Let's deploy a static Next.js app to an S3 Bucket. I will use Yandex Object Storage (an S3-compatible service by Yandex). But it could be any S3 service and any "static" web app (Vite React, bare HTML, etc.); the process will be very similar.
Create Next.js App
bun create next-app Check everything works as expected by running the dev server:
bun run dev Now we need to change the next.config.ts to:
import type { NextConfig } from "next"; const nextConfig: NextConfig = { /* config options here */ // cacheComponents: true, // partialPrefetching: true, output: 'export', distDir: 'dist', trailingSlash: true }; export default nextConfig; We enabled the static export by setting output: 'export'. Also, the output folder was changed to dist (instead of out by default). We also need trailingSlash: true to make the Next.js routes work without the server (change links from /me -> /me/ and emit /me.html -> /me/index.html). Please see the docs for more details.
Let's build the app:
bun run build The dist folder should appear.
Yandex Cloud Console
Open the Yandex Cloud Console.
Go to the Object Storage.
Create a new bucket (set Read objects -> For all).
Click the bucket you created. Go to Settings -> Website -> turn on Hosting. Set the Home page and Error page values and click Save.
Now our bucket is ready. 🎉
Deployment Script
Let's write a script to automate our deployment process. First, to be able to upload files to the bucket, we need a static key.
Create a Service Account and Assign Roles
- Navigate to Identity and Access Management -> Service accounts.
- Click
Create service account, name it (for example, s3-copy-sa), and clickCreate. - Assign the
storage.editorrole to the service account so it can manage and upload objects to your bucket.
Create the Static Access Key
- In the Service accounts list, select your newly created service account.
- In the top panel, click
Create new keyand selectCreate static access key. - Add a description (optional) and click
Create. - Important: Copy and save the Key ID and the Secret Key immediately. You will not be able to view the secret key again once you close the window.
Create the secrets/.env.deploy file:
export BUCKET_NAME=your-bucket-name export AWS_ACCESS_KEY_ID=your-access-key-id export AWS_SECRET_ACCESS_KEY=your-access-key export AWS_DEFAULT_REGION=ru-central1 export AWS_ENDPOINT_URL=https://storage.yandexcloud.net/ Add secrets/* to .gitignore so your secrets will never be committed to your git repository.
Create Script
Create the scripts/deploy.sh file:
source ./secrets/.env.deploy dist_folder="dist" is_dist_exists=0 if [ -d $dist_folder ]; then is_dist_exists=1 fi should_rebuild=1 if [ $is_dist_exists -ne 0 ]; then echo "" read -p " 🚧 The \"$dist_folder\" (build) folder already exists. Rebuild the project? (y/n): " confirm if [ "$confirm" == "n" ]; then should_rebuild=0 fi fi if [ $should_rebuild == 1 ]; then echo "" echo " 🏗️ building..." echo "" bun run build build_status=$? else build_status=0 fi if [ $build_status -ne 0 ]; then echo "" echo " 🚨 Build failed!" echo "" exit $build_status fi echo "" read -p " 🛳️ Deploy to production? (y/n): " confirm if [ "$confirm" != "y" ]; then echo " 🚫 Deploy canceled." echo "" exit 1 fi echo "" echo " ☕" echo "" echo "" echo " Backup old files..." echo "" (aws s3 --recursive mv s3://$BUCKET_NAME/ "s3://$BUCKET_NAME.backup/$(date +%Y-%m-%d_%H-%M-%S)/") echo "" echo "Copying new files..." echo "" (aws s3 --recursive cp $dist_folder s3://$BUCKET_NAME/) unset BUCKET_NAME unset AWS_ACCESS_KEY_ID unset AWS_SECRET_ACCESS_KEY unset AWS_DEFAULT_REGION unset AWS_ENDPOINT_URL echo "" echo " 🚀 Done!" echo "" This script will check if the dist folder exists. Ask whether we need to rebuild the project. Back up the old deployment to the BACKET_NAME.backup bucket. And upload the current build to the BUCKET_NAME bucket.
Please create the BUCKET_NAME.backup bucket. For example, if your bucket name is static-app, then create the static-app.backup bucket.
Also, install the aws-cli command-line tool. We will need it to manipulate our buckets. See the docs for more info.
curl -fsSL https://awscli.amazonaws.com/v2/install.sh | bash Make it Live
Let's allow our script to be executed:
chmod +x scripts/deploy.sh And now we are ready to run it:
./scripts/deploy.sh After the script finishes, we should see our build files inside the bucket.
Go to the Settings tab -> Website and click the Link (https://your-bucket-name.website.yandexcloud.net).
Congratulations, the app is live now! 🥳
Credits
Photo by Sixteen Miles Out on Unsplash.









