Skip to main content
Last Reviewed: September 13, 2021

Symlinks and Assumed Write Access

Learn how to create symbolic links from the code directory to a file.


Some modules and plugins create files within hard-coded paths outside of the standard path for the given framework, which can be problematic on Pantheon.

Standard File Path

Pantheon provides one location for files that are part of your site's content, like those that are managed through Drupal upload forms, e.g., user profile pictures: /sites/default/files. For Drupal sites, this is the default location for files that are uploaded as part of your application.

For WordPress sites, /wp-content/uploads is the default location for files. All other locations are considered part of your codebase, and under version control.

These directories are symbolically linked to Pantheon's cloud-based filesystem, Valhalla, which is writeable on all environments.

Extensions that create files within the codebase (e.g.,wp-content/plugins/plugin-name/some-other-directory, or /sites/all/modules/module-name/some-other-directory) incorrectly assume write access that is not granted on the Live and Test environments.

Custom configurations that use non-standard file paths (e.g.,sites/default/blogfiles) are also incompatible with Pantheon.

The best solution is to communicate with the maintainer of the module, plugin, or custom code/configuration and request that hard-coded, nonstandard paths be fixed. Alternatively, you can create a symbolic link (symlink) as a workaround to avoid failures on Test and Live.

Info:
Note

We do not recommend creating symlinks over SFTP due to inconsistencies between clients.

The following is for Mac and Linux only. Windows users may refer to Microsoft documentation for opening Command Prompt as an Administrator and creating symlinks using mklink or create symlinks within a virtual machine.

  1. On your Dev environment's Dashboard, change the Connection Mode from SFTP to Git mode. Install Git and clone the code locally if you have not done so already.

  2. From your terminal, cd to the site code repository:

    cd ~/sites/myawesomesite/ #Change this to your project directory.
  3. Move the directory you want to replace with a symlink. This serves to both back up any data that may otherwise be lost, and to prevent the symlink from being nested inside the existing directory:

    mv ./wp-content/path/plugin-expects-write-to ~/backups

    The command above moves the directory to a local backups directory in your home folder. Replace this with your preferred backup location. Note that this backup is now outside and separate from your site's codebase, and is only a safety measure to prevent data loss. Once you've confirmed that the symlink works across all environments and no data has been lost, you can remove this backup.

  4. Use the cd command to change to the location where the symlink will live. The symlink command (ln) is sensitive to the working directory - the folder your command line prompt is currently in. Working from the location of the symlink allows for correct relative paths:

    cd wp-content/path/
  5. Create a symlink for the standard files path:

    # The first path will be used as the new file destination instead of whatever path the plugin assumed write access to
    ln -s ../uploads/new-directory #The last nested directory should mirror the directory name the plugin expects to write to
    About ln Arguments

    The most common usage of ln is the form ln -s path/to/source.file path/to/destination.file. The -s flag creates a symbolic link, which is more like a redirect to the source, whereas a hard link is a new file sharing the same inode

    in the file system.

    By default, ln creates a file in the current working directory with the same name as the source. In the example above, we don't provide a destination file name as an argument. This simplifies the command when the link doesn't need a different name.

  6. Stage your changes:

    git add .
  7. Run git status to review your current index, then commit your changes:

    git commit -m "symlink non-standard files path to wp-content/uploads"
  8. Push the changes to Pantheon:

    git push origin master

    Your commit can be seen in the Dev environment's commit history. Once this commit is synced to all environments, the plugin will successfully write files within any environment, even when the Dev environment's connection mode is set to Git.

    You should not see the newly created files in the Dashboard as "ready to commit," as files are not version controlled. Only the symlink to the new path is in the codebase.

    Info:
    Note

    In our example, we set the target directory of the symlink as ./wp-content/uploads/new-directory. Make sure this directory is created via SFTP if it does not exist yet.

  9. Deploy to Test and confirm results.

  10. Deploy to Live and perform the plugin operation that creates the desired files, then confirm results.

You can follow the optional steps below to verify that your symlink is correct.

  1. In the terminal, cd to the symlinked path:

    cd /code/wp-content/cache
  2. Enter pwd to confirm you are in the path /files/cache.

    If the folder is symlinked correctly, an arrow will be displayed on the left side of the folder that is symlinked in your FTP or SFTP.

    If the symlink is incorrect, you will receive an error message.

  3. Click the arrow next to the folder.

    If you are directed to the files/cache folder, the symlink is correct.

Examples

Divi theme version 4.0.6 and above

As discussed in Modules and Plugins with Known Issues, Divi WordPress Theme & Visual Page Builder version 4.0.6 and above is assumes write access to the codebase where the et-cache folder is located.

Info:
Note

Manually create the target folders wp-content/et-cache for Dev, Test, Live, and any Multidev environments.

For MacOS & Linux

From the wp-content directory:

ln -s ./uploads/et-cache ./et-cache

To verify, use ls -al:

et-cache -> ./uploads/et-cache

For Windows

Note that the syntax for Windows Command Prompt is opposite from MacOS and Linux, requiring the symlink path before the target. From the root of your installation, run mklink as an admin:

mklink /d .\wp-content\et-cache .\uploads\et-cache

Each command will return the following upon success:

symbolic link created for .\wp-content\et-cache <<===>> .\uploads\et-cache

To verify that you have done it correctly, you should have these when you list your folders in wp-content directory: You can also verify success using dir:

<SYMLINKD>        et-cache [.\uploads\et-cache]
Nitropack

As discussed in WordPress Plugins and Themes with Known Issues, Nitropack assumes write access to the wp-content/nitropack folder and to advanced.cache.php.

Info:
Note

Manually create the target folders code/wp-content/uploads/nitropack and code/wp-content/uploads/advanced-cache.php for Dev, Test, and Live environments.

  1. In the command line, navigate to code/wp-content/uploads in your Dev environment. Or, if you are using an SFTP client (such as FileZilla), navigate to files/.

  2. Create a nitropack folder and an advanced-cache.php file. Be sure to delete any existing advanced-cache.php that is present in the ./uploads directory before creating the file:

    mkdir ./nitropack​ && touch ./advanced-cache.php
  3. Repeat steps 1 and 2 for your Test and Live environments.

  4. Navigate back to code/wp-content and create a symlink in your Dev environment:

    ln -s ./uploads/nitropack/ ./nitropack
    ln -s ./uploads/advanced-cache.php ./advanced-cache.php
  5. Commit changes to the Dev or Multidev environment, then deploy to Test to confirm the results before you deploy to Live.

WP-Rocket

As discussed in WordPress Plugins and Themes with Known Issues, WP-Rocket assumes write access to the codebase.

Info:
Note

Manually create the target folders wp-content/uploads/cache and wp-content/uploads/wp-rocket-config for Dev, Test, Live, and any Multidev environments.

For MacOS & Linux

From the wp-content directory:

ln -s ./uploads/cache ./cache
ln -s ./uploads/wp-rocket-config ./wp-rocket-config

To verify, use ls -al:

cache -> ./uploads/cache
wp-rocket-config -> ./uploads/wp-rocket-config

For Windows

Note that the syntax for Windows is opposite from MacOS and Linux, requiring the symlink path before the target:

mklink /d .\wp-content\cache .\uploads\cache
mklink /d .\wp-content\wp-rocket-config .\uploads\wp-rocket-config

Each command will return the following upon success:

symbolic link created for .\wp-content\cache <<===>> .\uploads\cache
symbolic link created for .\wp-content\wp-rocket-config <<===>> .\uploads\wp-rocket-config

To verify that you have done it correctly, you should have these when you list your folders in wp-content directory: You can also verify success using dir:

<SYMLINKD>        cache [.\uploads\cache]
<SYMLINKD>        wp-rocket-config [.\uploads\wp-rocket-config]
Uncode Theme

As discussed in WordPress Plugins and Themes with Known Issues, Uncode theme assumes write access to its CSS files and the codebase.

  1. Manually move the target folders. Note that Windows uses \ instead of / to separate directories. These examples are formatted or Mac and Linux:

    wp-content/themes/uncode/core/assets/css

    To: wp-content/uploads/uncode/assets/css

    And:

    wp-content/themes/uncode/library/css

    To: wp-content/uploads/uncode/library/css in Dev.

  2. Copy the files generated from:

    wp-content/themes/uncode/library/css

    To:

    wp-content/uploads/uncode/library/css

    In Test, Live, and any Multidev environments after deploying codes for the theme to take effect in different environments.

For MacOS & Linux

From the wp-content directory:

ln -s ../../../../uploads/uncode/assets/css ./themes/uncode/core/assets
ln -s ../../../uploads/uncode/library/css ./themes/uncode/library

To verify, use ls -al in the wp-content/themes/uncode/core/assets folder:

css -> ../../../../uploads/uncode/assets/css

As well as in the wp-content/themes/uncode/library folder:

css -> ../../../uploads/uncode/library/css

For Windows

Note that the syntax for Windows is opposite from MacOS and Linux, requiring the symlink path before the target and backslash (\) is used to denote folders. In the wp-content folder create the symlinks by:

mklink /d .\themes\uncode\core\assets ..\..\..\..\uploads\uncode\assets\css
mklink /d .\themes\uncode\library ..\..\..\uploads\uncode\library\css

Each command will return the following upon success:

symbolic link created for .\themes\uncode\core\assets <<===>> ..\..\..\..\uploads\uncode\assets\css
symbolic link created for .\themes\uncode\library <<===>> ..\..\..\uploads\uncode\library\css

To verify that you have done it correctly, you should have these when you list your folders in wp-content\themes\uncode\core\assets directory: You can also verify success using dir:

<SYMLINKD>        css [..\..\..\..\uploads\uncode\assets\css]

And in the themes\uncode\library directory:

<SYMLINKD>        css [..\..\..\uploads\uncode\library\css]

Troubleshooting

If a site no longer needs a symlink, because you uninstalled the plugin that required it for example, you can simply remove the symlink file from your codebase using rm. Move back any folders from the public files folder that should be tracked by version control.

Modules That Verify Directories

Some modules and plugins verify that the target directory exists using is_dir() which returns bool(false) if the directory is a symlink. It may help to patch the module/plugin to use is_link() instead of is_dir().

If a symlinked folder doesn't show the proper contents, double-check that the path is correct. In Bash, ls -l will show symlinks paths:

ls -l

lrwxr-xr-x  1 user  group     39 Sep 13 14:29 images -> ../plugins/some-plugin/images/

Try changing the working directory in which you create the symlink, using ../ to refer to directories above the working directory, and ./ to refer to the current directory.

More Resources

For more details on creating symbolic links on Mac/Linux, see this thread.