Symlinks and Assumed Write Access

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

Discuss in our Forum Discuss in Slack

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.


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
  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.


    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.



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.