---
description: >-
  The source argument tells OpenTofu where to find child modules's
  configurations in locations like GitHub, the OpenTofu Registry, Bitbucket,
  Git, Mercurial, S3, and GCS.
---

# Module Sources

The `source` argument in [a `module` block](../../language/modules/syntax.mdx)
tells OpenTofu where to find the source code for the desired child module.

OpenTofu uses this during the module installation step of `tofu init`
to download the source code to a directory on local disk so that other OpenTofu commands can use it.

The module installer supports installation from a number of different source
types.

- [Local paths](#local-paths)

- [Module Registry](#module-registry)

- [GitHub](#github)

- [Bitbucket](#bitbucket)

- Generic [Git](#generic-git-repository), [Mercurial](#generic-mercurial-repository) repositories

- [OCI Distribution](#oci-distribution-repository) repositories

- [HTTP URLs](#http-urls)

- [S3 buckets](#s3-bucket)

- [GCS buckets](#gcs-bucket)

- [Modules in Package Sub-directories](#modules-in-package-sub-directories)

Each of these is described in the following sections. Module source addresses
use a _URL-like_ syntax, but with extensions to support unambiguous selection
of sources and additional features.

We recommend using local file paths for closely-related modules used primarily
for the purpose of factoring out repeated code elements, and using a native
OpenTofu module registry for modules intended to be shared by multiple calling
configurations. We support other sources so that you can potentially distribute
OpenTofu modules internally with existing infrastructure.

Many of the source types will make use of "ambient" credentials available
when OpenTofu is run, such as from environment variables or credentials files
in your home directory. This is covered in more detail in each of the following
sections.

We recommend placing each module that is intended to be re-usable in the root
of its own repository or archive file, but it is also possible to
[reference modules from subdirectories](#modules-in-package-sub-directories).

## Local Paths

Local path references allow for factoring out portions of a configuration
within a single source repository.

```hcl
module "consul" {
  source = "./consul"
}
```

A local path must begin with either `./` or `../` to indicate that a local
path is intended, to distinguish from
[a module registry address](#module-registry).

Local paths are special in that they are not "installed" in the same sense
that other sources are: the files are already present on local disk (possibly
as a result of installing a parent module) and so can just be used directly.
Their source code is automatically updated if the parent module is upgraded.

Note that OpenTofu does not consider an _absolute_ filesystem path (starting
with a slash, a drive letter, or similar) to be a local path. Instead,
OpenTofu will treat that in a similar way as a remote module and copy it into
the local module cache. An absolute path is a "package" in the sense described
in [Modules in Package Sub-directories](#modules-in-package-sub-directories).
We don't recommend using absolute filesystem paths to refer to modules,
because it will tend to couple your configuration to the filesystem
layout of a particular computer.

## Module Registry

A module registry is the native way of distributing modules for use
across multiple configurations, using an OpenTofu-specific protocol that
has full support for module versioning.

The [Public OpenTofu Registry](https://registry.opentofu.org/) is an index of modules
shared publicly using this protocol. This public registry is the easiest way
to get started with OpenTofu and find modules created by others in the
community.

You can also use a
[private registry](../../internals/module-registry-protocol.mdx), either
via [TACOS](../../intro/tacos.mdx) (TF Automation and Collaboration Software), or by running a custom
service that implements
[the module registry protocol](../../internals/module-registry-protocol.mdx).

Modules on the public registry can be referenced using a registry
source address of the form `<NAMESPACE>/<NAME>/<PROVIDER>`, with each
module's information page on the registry site including the exact address
to use.

```hcl
module "consul" {
  source = "hashicorp/consul/aws"
  version = "0.1.0"
}
```

The above example will use the
[Consul module for AWS](https://github.com/hashicorp/terraform-aws-consul)
from a public registry.

For modules hosted in other registries, prefix the source address with an
additional `<HOSTNAME>/` portion, giving the hostname of the private registry:

```hcl
module "consul" {
  source = "app.terraform.io/example-corp/k8s-cluster/azurerm"
  version = "1.1.0"
}
```

Registry modules support versioning. You can provide a specific version as shown
in the above examples, or use flexible
[version constraints](../../language/modules/syntax.mdx#version).

You can learn more about the registry at the
[Module Registry documentation](../../internals/module-registry-protocol.mdx).

To access modules from a private registry, you may need to configure an access
token [in the CLI config](../../cli/config/config-file.mdx#credentials). Use the
same hostname as used in the module source string. For a private registry
within [TACOS](../../intro/tacos.mdx) (TF Automation and Collaboration Software), use the same authentication token as you would
use with the API or command-line clients.

## GitHub

OpenTofu will recognize unprefixed `github.com` URLs and interpret them
automatically as Git repository sources.

```hcl
module "consul" {
  source = "github.com/hashicorp/example"
}
```

The above address scheme will clone over HTTPS. To clone over SSH, use the
following form:

```hcl
module "consul" {
  source = "git@github.com:hashicorp/example.git"
}
```

These GitHub schemes are treated as convenient aliases for
[the general Git repository address scheme](#generic-git-repository), and so
they obtain credentials in the same way and support the `ref` argument for
selecting a specific revision. You will need to configure credentials in
particular to access private repositories.

## Bitbucket

OpenTofu will recognize unprefixed `bitbucket.org` URLs and interpret them
automatically as BitBucket repositories:

```hcl
module "consul" {
  source = "bitbucket.org/example-corp/tofu-consul-aws"
}
```

This shorthand works only for public repositories, because OpenTofu must
access the BitBucket API to learn if the given repository uses Git or Mercurial.

OpenTofu treats the result either as [a Git source](#generic-git-repository)
or [a Mercurial source](#generic-mercurial-repository) depending on the
repository type. See the sections on each version control type for information
on how to configure credentials for private repositories and how to specify
a specific revision to install.

## Generic Git Repository

Arbitrary Git repositories can be used by prefixing the address with the
special `git::` prefix. After this prefix, any valid
[Git URL](https://git-scm.com/docs/git-clone#_git_urls)
can be specified to select one of the protocols supported by Git.

For example, to use HTTPS or SSH:

```hcl
module "vpc" {
  source = "git::https://example.com/vpc.git"
}

module "storage" {
  source = "git::ssh://username@example.com/storage.git"
}
```

OpenTofu installs modules from Git repositories by running `git clone`, and
so it will respect any local Git configuration set on your system, including
credentials. To access a non-public Git repository, configure Git with
suitable credentials for that repository.

If you use the SSH protocol then any configured SSH keys will be used
automatically. This is the most common way to access non-public Git
repositories from automated systems because it allows access to private
repositories without interactive prompts.

If using the HTTP/HTTPS protocol, or any other protocol that uses
username/password credentials, configure
[Git Credentials Storage](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage)
to select a suitable source of credentials for your environment.

### Selecting a Revision

By default, OpenTofu will clone and use the default branch (referenced by
`HEAD`) in the selected repository. You can override this using the
`ref` argument. The value of the `ref` argument can be any reference that would be accepted
by the `git checkout` command, such as branch, SHA-1 hash (short or full), or tag names.
For a full list of the possible values, see
[Git Tools - Revision Selection](https://git-scm.com/book/en/v2/Git-Tools-Revision-Selection#_single_revisions)
in [the Git Book](https://git-scm.com/book/en/v2).

```hcl
# select a specific tag
module "vpc" {
  source = "git::https://example.com/vpc.git?ref=v1.2.0"
}

# directly select a commit using its SHA-1 hash
module "storage" {
  source = "git::https://example.com/storage.git?ref=51d462976d84fdea54b47d80dcabbf680badcdb8"
}
```

### Shallow Clone

For larger repositories you may prefer to make only a shallow clone in order
to reduce the time taken to retrieve the remote repository.

The `depth` URL argument corresponds to
[the `--depth` argument to `git clone`](https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---depthltdepthgt),
telling Git to create a shallow clone with the history truncated to only
the specified number of commits.

However, because shallow clone requires different Git protocol behavior,
setting the `depth` argument makes OpenTofu pass your [`ref` argument](#selecting-a-revision),
if any, to
[the `--branch` argument to `git clone`](https://git-scm.com/docs/git-clone#Documentation/git-clone.txt---branchltnamegt)
instead. That means it must specify a named branch or tag known to the remote
repository, and that raw commit IDs are not acceptable.

Because OpenTofu only uses the most recent selected commit to find the source
code of your specified module, it is not typically useful to set `depth`
to any value other than `1`.

### "scp-like" address syntax

When using Git over SSH, we recommend using the `ssh://`-prefixed URL form
for consistency with all of the other URL-like git address forms.
You may opt to use the alternative "scp-like" syntax instead, in which case you
must omit the `ssh://` scheme part and include only the `git::` part.
For example:

```hcl
module "storage" {
  source = "git::username@example.com:storage.git"
}
```

If you use the `ssh://` URL scheme then OpenTofu will assume that the colon
marks the beginning of a port number, rather than the beginning of the path.
This matches how Git itself interprets these different forms, aside from
the OpenTofu-specific `git::` selector prefix.

## Generic Mercurial Repository

You can use arbitrary Mercurial repositories by prefixing the address with the
special `hg::` prefix. After this prefix, any valid
[Mercurial URL](https://www.mercurial-scm.org/repo/hg/help/urls)
can be specified to select one of the protocols supported by Mercurial.

```hcl
module "vpc" {
  source = "hg::http://example.com/vpc.hg"
}
```

OpenTofu installs modules from Mercurial repositories by running `hg clone`, and
so it will respect any local Mercurial configuration set on your system,
including credentials. To access a non-public repository, configure Mercurial
with suitable credentials for that repository.

If you use the SSH protocol then any configured SSH keys will be used
automatically. This is the most common way to access non-public Mercurial
repositories from automated systems because it allows access to private
repositories without interactive prompts.

### Selecting a Revision

You can select a non-default branch or tag using the optional `ref` argument:

```hcl
module "vpc" {
  source = "hg::http://example.com/vpc.hg?ref=v1.2.0"
}
```

## OCI Distribution Repository

The OCI Distribution protocol is a generalization of the protocol originally
used by Docker for distributing container images. OpenTofu can install
module packages from specially-formatted artifacts published into repositories
in OCI Registries.

```hcl
module "example" {
  source = "oci://example.com/repository-name"
}
```

The domain name specified immediately after the `oci://` prefix is the OCI
Registry domain name. The remainder of the address specifies a specific
repository name within that repository.

If your specified OCI Registry requires authentication credentials,
refer to [OCI Registry Credentials](/cli/oci_registries/credentials.mdx).

### Selecting a Tag or Digest

By default, OpenTofu attempts to retrieve the artifact associated with the
remote tag named "latest". You can select a different artifact using
one of the following arguments in the query string:

- `tag` specifies a different tag name to use.
- `digest` directly specifies the digest of the manifest of the desired
  artifact, totally ignoring the tags defined in the repository.

Only one of these arguments at a time can be used in a valid source address.

For example, to select a tag named `v1.0.0`:

```hcl
module "example" {
  source = "oci://example.com/repository-name?tag=v1.0.0"
}
```

### Building a module package artifact

For more information on the manifest structure that OpenTofu expects for
module package artifacts, refer to
[Module Packages in OCI Registries](/cli/oci_registries/module-package.mdx).

## HTTP URLs

When you use an HTTP or HTTPS URL, OpenTofu will make a `GET` request to
the given URL, which can return _another_ source address. This indirection
allows using HTTP URLs as a sort of "vanity redirect" over a more complicated
module source address.

OpenTofu will append an additional query string argument `tofu-get=1` to
the given URL before sending the `GET` request, allowing the server to
optionally return a different result when OpenTofu is requesting it.

If the response is successful (`200`-range status code), OpenTofu looks in
the following locations in order for the next address to access:

- The value of a response header field named `X-Terraform-Get`.

- If the response is an HTML page, a `meta` element with the name `tofu-get`:

  ```html
  <meta name="tofu-get" content="github.com/hashicorp/example" />
  ```

In either case, the result is interpreted as another module source address
using one of the forms documented elsewhere on this page.

If an HTTP/HTTPS URL requires authentication credentials, use a `.netrc`
file to configure the credentials. By default, OpenTofu searches for the `.netrc` file
in your HOME directory. However, you can override the default filesystem location by setting the `NETRC` environment variable. For information on the `.netrc` format,
refer to [the documentation for using it in `curl`](https://everything.curl.dev/usingcurl/netrc).

### Fetching archives over HTTP

As a special case, if OpenTofu detects that the URL has a common file
extension associated with an archive file format then it will bypass the
special `tofu-get=1` redirection described above and instead just use
the contents of the referenced archive as the module source code:

```hcl
module "vpc" {
  source = "https://example.com/vpc-module.zip"
}
```

The extensions that OpenTofu recognizes for this special behavior are:

- `zip`
- `tar.bz2` and `tbz2`
- `tar.gz` and `tgz`
- `tar.xz` and `txz`

If your URL _doesn't_ have one of these extensions but refers to an archive
anyway, use the `archive` argument to force this interpretation:

```hcl
module "vpc" {
  source = "https://example.com/vpc-module?archive=zip"
}
```

:::note
If the content of the archive file is a directory, you will need to
include that directory in the module source. Read the section on
[Modules in Package Sub-directories](#modules-in-package-sub-directories) for more
information.
:::

## S3 Bucket

You can use archives stored in S3 as module sources using the special `s3::`
prefix, followed by
[an S3 bucket object URL](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingBucket.html).

```hcl
module "consul" {
  source = "s3::https://s3-eu-west-1.amazonaws.com/examplecorp-tofu-modules/vpc.zip"
}
```

:::note
Buckets in AWS's us-east-1 region must use the hostname `s3.amazonaws.com` (instead of `s3-us-east-1.amazonaws.com`).
:::

The `s3::` prefix causes OpenTofu to use AWS-style authentication when
accessing the given URL. As a result, this scheme may also work for other
services that mimic the S3 API, as long as they handle authentication in the
same way as AWS.

The resulting object must be an archive with one of the same file
extensions as for [archives over standard HTTP](#fetching-archives-over-http).
OpenTofu will extract the archive to obtain the module source tree.

The module installer looks for AWS credentials in the following locations,
preferring those earlier in the list when multiple are available:

- The `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables.
- The default profile in the `.aws/credentials` file in your home directory.
- If running on an EC2 instance, temporary credentials associated with the
  instance's IAM Instance Profile.

## GCS Bucket

You can use archives stored in Google Cloud Storage as module sources using the special `gcs::`
prefix, followed by
[a GCS bucket object URL](https://cloud.google.com/storage/docs/request-endpoints#typical).

For example

- `gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH_TO_MODULE`
- `gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH/TO/module.zip`

```hcl
module "consul" {
  source = "gcs::https://www.googleapis.com/storage/v1/modules/foomodule.zip"
}
```

The module installer uses Google Cloud SDK to authenticate with GCS. You can
use any of the following methods to set Google Cloud Platform credentials:

* Set the `GOOGLE_OAUTH_ACCESS_TOKEN` environment variable to a raw Google Cloud Platform OAuth access token.
* Enter the path of your service account key file in the `GOOGLE_APPLICATION_CREDENTIALS` environment variable.
* If you're running OpenTofu from a GCE instance, default credentials are automatically available. See [Creating and Enabling Service Accounts](https://cloud.google.com/compute/docs/access/create-enable-service-accounts-for-instances) for Instances for more details.
* On your computer, you can make your Google identity available by running `gcloud auth application-default login`.

## Modules in Package Sub-directories

When the source of a module is a version control repository or archive file
(generically, a "package"), the module itself may be in a sub-directory relative
to the root of the package.

A special double-slash syntax is interpreted by OpenTofu to indicate that
the remaining path after that point is a sub-directory within the package.
For example:

- `hashicorp/consul/aws//modules/consul-cluster`
- `git::https://example.com/network.git//modules/vpc`
- `oci://example.com/repository-name//modules/vpc`
- `https://example.com/network-module.zip//modules/vpc`
- `s3::https://s3-eu-west-1.amazonaws.com/examplecorp-tofu-modules/network.zip//modules/vpc`

If the source address has arguments, such as the `ref` argument supported for
the version control sources, the sub-directory portion must be _before_ those
arguments:

- `git::https://example.com/network.git//modules/vpc?ref=v1.2.0`
- `github.com/hashicorp/example//modules/vpc?ref=v1.2.0`
- `oci://example.com/repository-name//modules/vpc?tag=v1.2.0`

OpenTofu will still extract the entire package to local disk, but will read
the module from the subdirectory. As a result, it is safe for a module in
a sub-directory of a package to use [a local path](#local-paths) to another
module as long as it is in the _same_ package.

## Support for Variable and Local Evaluation

As projects grow in complexity and requirements, it is prudent to consider using locals and variables in the module source and version fields.

Many organizations utilize the mono-repo pattern for modules:
```hcl
locals {
	modules_repo = "github.com/myorg/tofu-modules/"
	modules_version = "?ref=v1.20.4"
}

module "storage" {
  source = "${local.modules_repo}/storage${local.modules_version}"
}

module "compute" {
  source = "${local.modules_repo}/compute${local.modules_version}"
}
```
It is quite easy to then update the version for a patch release, or to switch to a fork of the repository.

:::note
The source and version fields may not contain any references to data in the state or provider defined functions. All values must be able to be resolved during `tofu init` before the state is available.
:::
