Upload static website to IPFS pinning services and optionally update DNS.
The goal of ipfs-deploy
is to make it as easy as possible to
deploy a static website to IPFS.
npm install -g ipfs-deploy
Or
yarn global add ipfs-deploy
You can call it either as ipd
or as ipfs-deploy
:
ipd public/
ipfs-deploy public/
You can run it directly with npx without needing to install anything:
npx ipfs-deploy _site
It will deploy to a public pinning service and give you a link to
ipfs.io/ipfs/your-hash
so you can check it out.
You can get started just by typing out ipd
and it will have smart defaults.
By default, it deploys to Infura, which doesn't need signup and you'll get a
link like ipfs.io/ipfs/QmHash
that you can use to see if everything went ok.
When you don't specify a path argument to deploy, ipfs-deploy
tries to
guess it for you based on the build directories used by the most popular static
site generators by the following order:
Path | Static generators |
---|---|
_site |
jekyll, hakyll, eleventy |
site |
some others |
public |
gatsby, hugo, hexo |
dist |
nuxt |
output |
pelican |
build |
create-react-app, metalsmith, middleman |
website/build |
docusaurus |
docs |
many others |
Some pinning services and DNS providers require signup and additional
environment variables to be set. We support and use .env
files. Read
the section bellow to find out about which services are supported and
how to enable them.
For further information about the CLI, please run ipfs-deploy --help
.
Some things to keep in mind:
- Please note the
__
(double underscore) between some words (such as afterPINATA
andCLOUDFLARE
). - Don't commit the
.env
file to source control unless you know what you're doing.
These services are subject to their terms. Not a decentralization nirvana by any stretch of the imagination, but a nice way to get started quickly with a blog, static website, or frontend web app.
Infura is a freemium pinning service that doesn't require any additional setup. It's the default one used. Please bear in mind that Infura is a free service, so there is probably a rate-limiting.
Use flag -p infura
.
Pinata is another freemium pinning service. It gives you more control over what's uploaded. You can delete, label and add custom metadata. This service requires signup.
IPFS_DEPLOY_PINATA__API_KEY=<api key>
IPFS_DEPLOY_PINATA__SECRET_API_KEY=<secret api key>
Use flag -p pinata
.
Fission is a backend-as-a-service that uses IPFS and supports pinning. This service requires signup.
IPFS_DEPLOY_FISSION__USERNAME=<username>
IPFS_DEPLOY_FISSION__PASSWORD=<password>
Use flag -p fission
.
You can use IPFS Cluster to pin your website. It can be either self-hosted or just any IPFS Cluster you want.
IPFS_DEPLOY_IPFS_CLUSTER__HOST=<multiaddr>
IPFS_DEPLOY_IPFS_CLUSTER__USERNAME=<basic auth username>
IPFS_DEPLOY_IPFS_CLUSTER__PASSWORD=<basic auth password>
Use flag -p ipfs-cluster
.
DAppNode is not a centralized IPFS provider. It is an operation system that
allows you to effortless host a number of decentralized apps on your own hardware.
Default installation of DAppNode includes an IPFS node, available via VPN at ipfs.dappnode
.
If you can't reach the node make sure that you are connected to your DAppNode VPN.
Use flag -p dappnode
.
Cloudflare is a freemium DNS provider. Supports CNAME flattening for naked domains and integrates with their IPFS gateway at cloudflare-ipfs.com.
Bear in mind that Cloudflare IPFS doesn't host the content itself (it's a cached gateway), so a stable pinning service is needed if you don't want to rely on your computer's IPFS daemon's availability to serve your website.
In order to use a Cloudflare API token you need to grant zone read and dns edit permissions (both under the zone section). You also need to not restrict the zone resources to a specific zone. (This is because the list zones API call doesn't work if you only allow access to a specific zone and that is needed to look up the id of the zone you specify.)
# credentials
IPFS_DEPLOY_CLOUDFLARE__API_EMAIL=
IPFS_DEPLOY_CLOUDFLARE__API_KEY=
# or...
IPFS_DEPLOY_CLOUDFLARE__API_TOKEN=
# dns info
IPFS_DEPLOY_CLOUDFLARE__ZONE=
IPFS_DEPLOY_CLOUDFLARE__RECORD=
Example with top-level domain:
# cloudflare dns info
IPFS_DEPLOY_CLOUDFLARE__ZONE=agentofuser.com
IPFS_DEPLOY_CLOUDFLARE__RECORD=_dnslink.agentofuser.com
Example with subdomain:
# cloudflare dns info
IPFS_DEPLOY_CLOUDFLARE__ZONE=agentofuser.com
IPFS_DEPLOY_CLOUDFLARE__RECORD=_dnslink.mysubdomain.agentofuser.com
Use flag -d cloudflare
.
DNSimple is a paid-for DNS provider. They have no specific IPFS support, but allow the setting of DNS TXT records which underlies IPFS DNSLink.
# credentials
IPFS_DEPLOY_DNSIMPLE__TOKEN=
# dns info
IPFS_DEPLOY_DNSIMPLE__ZONE=
IPFS_DEPLOY_DNSIMPLE__RECORD=
Example with top-level domain:
# dnsimple dns info
IPFS_DEPLOY_DNSIMPLE__ZONE=agentofuser.com
IPFS_DEPLOY_DNSIMPLE__RECORD=_dnslink.agentofuser.com
Example with subdomain:
# dnsimple dns info
IPFS_DEPLOY_DNSIMPLE__ZONE=agentofuser.com
IPFS_DEPLOY_DNSIMPLE__RECORD=_dnslink.mysubdomain.agentofuser.com
Use flag -d dnsimple
.
This is still pretty unstable and subject to change, so I will just show how the executable currently uses the API.
const deploy = require('ipfs-deploy')
;(async () => {
try {
const deployOptions = {
publicDirPath: argv.path,
copyHttpGatewayUrlToClipboard:
!(argv.clipboard === false) && !argv.C && !argv.noClipboard,
open: !(argv.open === false) && !argv.O && !argv.noOpen,
remotePinners: argv.pinner,
dnsProviders: argv.dns,
siteDomain: argv.siteDomain,
credentials: {
cloudflare: {
apiKey: argv.cloudflare && argv.cloudflare.apiKey,
apiToken: argv.cloudflare && argv.cloudflare.apiToken,
apiEmail: argv.cloudflare && argv.cloudflare.apiEmail,
zone: argv.cloudflare && argv.cloudflare.zone,
record: argv.cloudflare && argv.cloudflare.record,
},
dnsimple: {
token: argv.dnsimple && argv.dnsimple.token,
zone: argv.dnsimple && argv.dnsimple.zone,
record: argv.dnsimple && argv.dnsimple.record
},
pinata: {
apiKey: argv.pinata && argv.pinata.apiKey,
secretApiKey: argv.pinata && argv.pinata.secretApiKey,
},
fission: {
username: argv.fission && argv.fission.username,
password: argv.fission && argv.fission.password,
},
ipfsCluster: {
host: argv.ipfsCluster && argv.ipfsCluster.host,
username: argv.ipfsCluster && argv.ipfsCluster.username,
password: argv.ipfsCluster && argv.ipfsCluster.password,
},
},
}
deploy(deployOptions)
} catch (e) {}
})()
We use dotenv
to handle credentials. Don't commit your .env
file to source
control.
This project was initially started by @agentofuser, who made a lot of awesome work in here. Posteriorly, it was transferred to ipfs-shipyard. Thanks for starting this awesome project!
Everyone is welcome to contribute and add new features! See everyone who has contributed!
To add support to a new pinning service, you must start by creating a file with
the name of the pinning service. Let's say it's called PinFree: create a file
called src/pinners/pinfree.js
with a content similar to this one:
module.exports = {
name: 'PinFree',
builder: opts => {
// Validate the options. If bad, throw.
// Return an api or the options you want to use later.
return api
},
pinDir: async (api, dir, tag) => {
// Pin a directory asynchronously, using the api
// returned by builder and a tag.
return hash
},
pinHash: async (api, hash, tag) => {
// Pin an hash asynchronously, using the api
// returned by builder and a tag.
// Just throw an error if the service doesn't
// support this action.
},
}
Now, you have your pinner service almost set up. Go to src/pinners/index.js
and add your pinner like this to the exports:
pinfree: makePinner(require('./pinfree')),
Finally, go to bin/ipfs-deploy.js
and add pinfree
to the list of supported
pinners. Also, do not forget to update the README with the new options.
To add support to a new DNS service, you must start by creating a file with the
name of the DNS service. Let's say it's called DNSFree: create a file called
dnsfree.js
with a content similar to this one:
module.exports = {
name: 'DNSFree',
validate: opts => {
// Validate the options. If bad, throw.
},
link: async (domain, hash, opts) => {
// DNSLink the domain to the hash using the
// validated options.
return {
record: someValue,
value: someOtherValue,
}
},
}
Now, you have your DNS service almost set up. Go to src/dnslink/index.js
and
add your pinner like this to the exports:
dnsfree: makeDnslink(require('./dnsfree')),
Finally, go to bin/ipfs-deploy.js
and add dnsfree
to the list of supported
DNS providers. Also, do not forget to update the README with the new options.
- agentofuser.com
- jaack.me
- Your website here
If you use this package to deploy your website, please send a pull request so I can add it to the Users section in the README. (I reserve the right to exercise discretion.)
BlueOak-1.0.0 OR BSD-2-Clause-Patent OR MIT © Agent of User
(The first two are the most permissive possible ever, more than MIT, which doesn't have a patent waiver. Use whichever satisfies your lawyer better.)