IMPORTANT: The Bazel docs have moved! Please update your bookmark to

You can read about the migration, and let us know what you think.


This page covers visibility specifications, best practices, and examples.

Visibility controls whether a target can be used (depended on) by targets in other packages. This helps other people distinguish between your library’s public API and its implementation details, and is an important tool to help enforce structure as your workspace grows.

If you need to disable the visibility check (for example when experimenting), use --check_visibility=false.

For more details on package and subpackages, see Concepts and terminology.

Visibility specifications

All rule targets have a visibility attribute that takes a list of labels. One target is visible to another if they are in the same package, or if they are granted visibility by one of the labels.

Each label has one of the following forms:

  • "//visibility:public": Anyone can use this target. (May not be combined with any other specification.)

  • "//visibility:private": Only targets in this package can use this target. (May not be combined with any other specification.)

  • "//foo/bar:__pkg__": Grants access to targets defined in //foo/bar (but not its subpackages). Here, __pkg__ is a special piece of syntax representing all of the targets in a package.

  • "//foo/bar:__subpackages__": Grants access to targets defined in //foo/bar, or any of its direct or indirect subpackages. Again, __subpackages__ is special syntax.

  • "//foo/bar:my_package_group": Grants access to all of the packages named by the given package group.

    • Package groups do not support the special __pkg__ and __subpackages__ syntax. Within a package group, "//foo/bar" is equivalent to "//foo/bar:__pkg__" and "//foo/bar/..." is equivalent to "//foo/bar:__subpackages__".

For example, if //some/package:mytarget has its visibility set to [":__subpackages__", "//tests:__pkg__"], then it could be used by any target that is part of the //some/package/... source tree, as well as targets defined in //tests/BUILD, but not by targets defined in //tests/integration/BUILD.

As a special case, package_group targets themselves do not have a visibility attribute; they are always publicly visible.

Visibility cannot be set to specific non-package_group targets. That triggers a “Label does not refer to a package group” or “cycle in dependency graph” error.

Visibility of a rule target

If a rule target does not set the visibility attribute, its visibility is given by the default_visibility that was specified in the package statement of the target’s BUILD file. If there is no such default_visibility declaration, the visibility is //visibility:private.

config_setting visibility has historically not been enforced. --incompatible_enforce_config_setting_visibility and --incompatible_config_setting_private_default_visibility provide migration logic for converging with other rules.

If --incompatible_enforce_config_setting_visibility=false, every config_setting is unconditionally visible to all targets.

Else if --incompatible_config_setting_private_default_visibility=false, any config_setting that doesn’t explicitly set visibility is //visibility:public (ignoring package default_visibility).

Else if --incompatible_config_setting_private_default_visibility=true, config_setting uses the same visibility logic as all other rules.

Best practice is to treat all config_setting targets like other rules: explicitly set visibility on any config_setting used anywhere outside its package.


File //frobber/bin/BUILD:

# This target is visible to everyone
    name = "executable",
    visibility = ["//visibility:public"],
    deps = [":library"],

# This target is visible only to targets declared in the same package
    name = "library",
    # No visibility -- defaults to private since no
    # package(default_visibility = ...) was used.

# This target is visible to targets in package //object and //noun
    name = "subject",
    visibility = [

# See package group "//frobber:friends" (below) for who can
# access this target.
    name = "thingy",
    visibility = ["//frobber:friends"],

File //frobber/BUILD:

# This is the package group declaration to which target
# //frobber/bin:thingy refers.
# Our friends are packages //frobber, //fribber and any
# subpackage of //fribber.
    name = "friends",
    packages = [

Visibility of a file target

By default, file targets are visible only from the same package. To make a file accessible from another package, use exports_files.

If the call to exports_files specifies the visibility attribute, that visibility applies. Otherwise, the file is public (the default_visibility is ignored).

When possible, prefer exposing a library or another type of rule instead of a source file. For example, declare a java_library instead of exporting a .java file. It’s good form for a rule target to only directly include sources in its own package.


File //frobber/data/BUILD:


File //frobber/bin/BUILD:

  name = "my-program",
  data = ["//frobber/data:readme.txt"],

Legacy behavior

If the flag --incompatible_no_implicit_file_export is not set, a legacy behavior applies instead.

With the legacy behavior, files used by at least one rule target in the package are implicitly exported using the default_visibility specification. See the design proposal for more details.

Visibility of bzl files

load statements are currently not subject to visibility. It is possible to load a bzl file anywhere in the workspace.

However, users may choose to run the Buildifier linter. The bzl-visibility check provides a warning if users load from beneath a subdirectory named internal or private.

Visibility of implicit dependencies

Some rules have implicit dependencies. For example, a C++ rule might implicitly depend on a C++ compiler.

Currently, implicit dependencies are treated like normal dependencies. They need to be visible by all instances of the rule. This behavior can be changed using --incompatible_visibility_private_attributes_at_definition.

Best practices

  • Avoid setting the default visibility to public. It may be convenient for prototyping or in small codebases, but it is discouraged in large codebases: try to be explicit about which targets are part of the public interface.

  • Use package_group to share the visibility specifications among multiple targets. This is especially useful when targets in many BUILD files should be exposed to the same set of packages.

  • Use fine-grained visibility specifications when deprecating a target. Restrict the visibility to the current users to avoid new dependencies.