Deploy a Hugo Site Safely with hugo deploy

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.

1. Check Hugo and choose the site

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.

2. Add one deployment target

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.

3. Inspect the deployment options

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.

4. Preview the remote changes

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.

5. Deploy with a deletion guard

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.

6. Verify the result and recover from a bad deployment

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.

Done means