Skip to content

Publish a Pack to CDN

A Spatial Pack on your local machine is useful for development. Publishing it to a CDN makes it accessible to web applications, remote teams, and API consumers. The spatialpack pack publish command uploads all pack files with correct content types, rewrites manifest URIs to CDN URLs, and verifies integrity before upload.

This recipe walks through the publish workflow, covering the pack lifecycle from built pack to live CDN.

  • spatialpack CLI installed (pip install -e ".[full]")
  • A built Spatial Pack (output from spatialpack pack build)
  • CDN credentials configured (AWS profile with S3 write access)
  • An S3 bucket configured for static file serving

Confirm your pack directory contains a valid manifest and all declared layer files.

Terminal window
spatialpack validate ./my-solar-pack/

Expected output:

Validating ./my-solar-pack/
Manifest: spatialpack.json (valid)
Layers: 6/6 files present
Hashes: 6/6 verified
License: CC-BY-4.0 (all sources)
Result: PASS

What just happened: The validator checked the manifest against the Spatial Pack schema, confirmed every declared layer file exists on disk, and verified integrity hashes. A pack must pass validation before it can be published.

See exactly what files will be uploaded and where they will land, without actually uploading anything.

Terminal window
spatialpack pack publish ./my-solar-pack/ \
--bucket my-packs-cdn \
--dry-run

Expected output:

Dry run: publish ./my-solar-pack/ -> s3://my-packs-cdn/packs/
Files to upload:
packs/spatial.properties:wa:solar-feasibility:v1/2025.01.31/spatialpack.json (12 KB)
packs/spatial.properties:wa:solar-feasibility:v1/2025.01.31/layers/cadastre.parquet (4.2 MB)
packs/spatial.properties:wa:solar-feasibility:v1/2025.01.31/layers/cadastre.pmtiles (8.1 MB)
packs/spatial.properties:wa:solar-feasibility:v1/2025.01.31/layers/roads.parquet (18.7 MB)
packs/spatial.properties:wa:solar-feasibility:v1/2025.01.31/layers/roads.pmtiles (12.3 MB)
Total: 5 files, 43.3 MB
Manifest URIs will be rewritten to: https://cdn.spatial.properties/packs/...
No files uploaded (dry run).

What just happened: The CLI calculated the S3 key paths based on the pack’s ID and version, checked file sizes, and showed the CDN URL that would be written into the manifest. The versioned path (pack_id/version/) ensures immutability — each version gets its own directory.

Run the publish command without --dry-run to upload.

Terminal window
spatialpack pack publish ./my-solar-pack/ \
--bucket my-packs-cdn

Expected output:

Publishing ./my-solar-pack/ to s3://my-packs-cdn/packs/
Uploading:
[1/5] spatialpack.json (12 KB) ... done
[2/5] layers/cadastre.parquet (4.2 MB) ... done
[3/5] layers/cadastre.pmtiles (8.1 MB) ... done
[4/5] layers/roads.parquet (18.7 MB) ... done
[5/5] layers/roads.pmtiles (12.3 MB) ... done
Manifest URIs rewritten to CDN domain: cdn.spatial.properties
Pack published: https://cdn.spatial.properties/packs/spatial.properties:wa:solar-feasibility:v1/2025.01.31/

What just happened: The CLI uploaded each file to S3 with the correct Content-Type header (e.g., application/x-parquet for GeoParquet, application/octet-stream for PMTiles) and appropriate Cache-Control headers. It then rewrote the manifest’s layer URIs from relative paths to full CDN URLs so consumers can fetch layers directly.

If you host packs on a custom domain, use the --cdn-domain flag to control URI rewriting.

Terminal window
spatialpack pack publish ./my-solar-pack/ \
--bucket my-packs-cdn \
--cdn-domain packs.example.com

What just happened: The manifest URIs were rewritten to use packs.example.com instead of the default domain. This is useful when serving packs through your own CloudFront distribution or custom CDN setup.

For multi-account setups, specify the AWS profile and region.

Terminal window
spatialpack pack publish ./my-solar-pack/ \
--bucket my-packs-cdn \
--profile production \
--region ap-southeast-2

What just happened: The CLI used the production AWS profile from your credentials file instead of the default profile. The --region flag targets the bucket’s AWS region.

After publishing, confirm the manifest is accessible at the CDN URL.

Terminal window
curl -s https://cdn.spatial.properties/packs/spatial.properties:wa:solar-feasibility:v1/2025.01.31/spatialpack.json | head -20

Expected output:

{
"pack_id": "spatial.properties:wa:solar-feasibility:v1",
"version": "2025.01.31",
"layers": [
{
"id": "cadastre",
"parquet": "https://cdn.spatial.properties/packs/.../layers/cadastre.parquet"
}
]
}

What just happened: The manifest is live on the CDN with layer URIs pointing to the published files. Any consumer — a web application, a Python script, or a DuckDB query — can fetch this manifest, discover the available layers, and load them directly.