hugo deploy can upload and delete files on a live bucket in one go, so this dry-runs it first and adds a deletion guard before the real push. You will configure a deployment target, preview the synchronisation, and deploy a local public directory to a supported cloud bucket. The examples use Hugo 0.123.7, installed from the Ubuntu hugo package on this machine. Allow about 20 minutes for the first deployment, plus time to create and authenticate the cloud account.
This workflow assumes that you already have a Hugo site, a bucket with the provider, and credentials for that provider. Hugo's deploy documentation covers Amazon S3, Azure Blob Storage, and Google Cloud Storage. Authentication happens through the provider's own CLI or environment, not through a secret placed in the Hugo configuration file.
Run these commands from the root of the site you intend to publish. They are ordinary, read-only checks:
$ hugo version
hugo v0.123.7+extended linux/amd64 BuildDate=2026-03-17T19:51:14Z VendorInfo=ubuntu:0.123.7-1ubuntu0.3+esm2
$ test -f hugo.yaml || test -f hugo.toml || test -f config.toml
$ hugo --quiet
$ test -s public/index.html && echo "built site is ready"
built site is ready
The exact version line depends on your package build. The last command confirms that the build produced a non-empty entry page. If your site uses a different publish directory, check the site's Hugo configuration before continuing. Do not deploy from a directory containing an unreviewed build.
Checkpoint: You are in the correct site directory and can identify the files that will be uploaded.
Hugo reads deployment targets from the deployment section of the project configuration. The required fields are a target name and a provider url. This S3 example uses a deliberately obvious placeholder:
deployment:
targets:
- name: production
url: s3://REPLACE_WITH_BUCKET?region=eu-west-2
Replace the bucket name and region with values that exist in your account. Keep the target name stable, since you will select it with --target. YAML, TOML and JSON are supported by Hugo, but the surrounding syntax must match the file you already use: do not copy the YAML block into a TOML file.
--target once you have more than one: without it, Hugo silently uses the first configured target, which gets risky once a staging target exists.hugo deploy.Confirm the installed command's defaults before making a remote change:
$ hugo deploy --help
Usage:
hugo deploy [flags] [args]
Flags:
--confirm ask for confirmation before making changes to the target
--dryRun dry run
--force force upload of all files
--invalidateCDN invalidate the CDN cache listed in the deployment target (default true)
--maxDeletes int maximum # of files to delete, or -1 to disable (default 256)
--target string target deployment from deployments section in config file; defaults to the first one
--workers int number of workers to transfer files. defaults to 10 (default 10)
The spelling and capitalisation are part of this installed CLI: use --dryRun, not --dry-run. The inherited flags also let you select a config file, environment, source directory or log level. Use them only when you have checked which site and configuration they select.
Safety boundary: Deployment can upload files and delete remote files. Start with a dry run against the named target:
$ hugo deploy --target=production --dryRun
Hugo prints the proposed changes for this site and target. The exact listing depends on your bucket and local build, so review the real output rather than expecting sample lines.
Hugo compares local and remote file names, sizes and MD5 checksums. Remote files absent from the local publish directory are candidates for deletion, subject to the deployment configuration and delete limit.
Pause here if the target name, bucket, file list or proposed deletions are unexpected. Fix the local build or configuration and repeat the dry run: a dry run changes neither the local publish directory nor the remote target.
Checkpoint: The dry-run file list contains the site you meant to publish, and every proposed deletion is understood.
Once the preview is correct, run the same target with a conservative delete limit:
$ hugo deploy --target=production --maxDeletes=20 --workers=4
The limit applies to remote files Hugo would delete in this deployment. The default is 256; setting a lower value makes an unexpectedly large removal fail rather than silently continuing. Use --confirm if you want Hugo to display the detected changes and ask before applying them:
$ hugo deploy --target=production --confirm --maxDeletes=20
--force uploads every file even when Hugo detects no local or remote difference. It is useful for a deliberate repair, but it increases transfer work and does not make deletion review unnecessary. --invalidateCDN defaults to true when the target contains CDN invalidation settings: leave it enabled when stale cached pages would be harmful, and disable it explicitly only when you understand the provider cost and caching consequences.
Do not use sudo for Hugo deployment. The command normally needs access to the site files and provider credentials belonging to your user, not root privileges.
Check the command's exit status immediately, then request a fresh copy of the public URL:
$ status=$?
$ test "$status" -eq 0 && echo "hugo deploy completed"
hugo deploy completed
$ curl --fail --silent --show-error https://www.example.test/ > /tmp/hugo-home.html
$ head -n 5 /tmp/hugo-home.html
Replace the URL with your own site. Also check a representative asset and an article route. A zero exit status confirms that Hugo completed its operation; it does not prove that DNS, CDN propagation or every browser route is correct.
Recovery: there is no generic undo flag. If a deployment removed or replaced the wrong objects, stop publishing, preserve the dry-run output and restore the intended files from version control or your site backup, then deploy that corrected build. Provider versioning or object recovery tools may help, but their use is provider-specific. Do not delete the bucket to try to reset the site.