When working with Chef templates, a common pitfall occurs when the target directory structure doesn't exist. The template resource will fail if parent directories aren't present, unlike some other configuration management tools that automatically create required directories.
template "/var/www/myapp/shared/config/database.yml" do
source 'database.yml.erb'
# ... other attributes ...
end
The proper Chef way to handle this is by explicitly declaring directory resources for each required parent directory:
directory "#{node[:app][:deploy_to]}/#{node[:app][:name]}/shared/config" do
owner node[:user][:username]
group node[:user][:username]
mode '0755'
recursive true
action :create
end
When implementing directory creation in your recipes:
- Set
recursive true
to create all parent directories - Ensure proper permissions (owner/group/mode) match your security requirements
- Consider using
not_if
oronly_if
guards for conditional creation
Here's a full implementation pattern:
# Create parent directories first
directory "#{node[:app][:deploy_to]}" do
owner node[:user][:username]
group node[:user][:username]
mode '0755'
recursive true
end
directory "#{node[:app][:deploy_to]}/#{node[:app][:name]}" do
owner node[:user][:username]
group node[:user][:username]
mode '0755'
end
directory "#{node[:app][:deploy_to]}/#{node[:app][:name]}/shared" do
owner node[:user][:username]
group node[:user][:username]
mode '0755'
end
directory "#{node[:app][:deploy_to]}/#{node[:app][:name]}/shared/config" do
owner node[:user][:username]
group node[:user][:username]
mode '0755'
end
# Then create the template
template "#{node[:app][:deploy_to]}/#{node[:app][:name]}/shared/config/database.yml" do
source 'database.yml.erb'
owner node[:user][:username]
group node[:user][:username]
mode '0644'
variables({
# ... template variables ...
})
end
For frequent use cases, consider creating a custom resource:
# libraries/ensure_directory.rb
module MyCookbook
module Helpers
def ensure_directory(path, owner, group, mode)
directory path do
owner owner
group group
mode mode
recursive true
action :create
end
end
end
end
When working with Chef templates, a common issue arises when the target directory structure doesn't exist yet. The template resource will fail if parent directories are missing, which is exactly what happens in this database.yml example:
template "#{node[:app][:deploy_to]}/#{node[:app][:name]}/shared/config/database.yml" do
source 'database.yml.erb'
owner node[:user][:username]
group node[:user][:username]
mode 0644
variables({
:environment => node[:app][:environment],
:adapter => node[:database][:adapter],
:database => node[:database][:name],
:username => node[:database][:username],
:password => node[:database][:password],
:host => node[:database][:host]
})
end
Chef provides a simple way to handle this using the directory
resource. You should create the directory structure before attempting to place files in it:
directory "#{node[:app][:deploy_to]}/#{node[:app][:name]}/shared/config" do
owner node[:user][:username]
group node[:user][:username]
mode 0755
recursive true
action :create
end
Here's how to properly combine both resources in your recipe:
# First ensure directory exists
config_dir = "#{node[:app][:deploy_to]}/#{node[:app][:name]}/shared/config"
directory config_dir do
owner node[:user][:username]
group node[:user][:username]
mode 0755
recursive true
end
# Then create the template file
template "#{config_dir}/database.yml" do
source 'database.yml.erb'
owner node[:user][:username]
group node[:user][:username]
mode 0644
variables({
:environment => node[:app][:environment],
:adapter => node[:database][:adapter],
:database => node[:database][:name],
:username => node[:database][:username],
:password => node[:database][:password],
:host => node[:database][:host]
})
end
- Always use
recursive true
when creating nested directory structures - Set appropriate permissions (mode) for directories (usually 755) vs files (644)
- Consider extracting directory paths into variables for maintainability
- The directory resource is idempotent - it won't fail if the directory exists
For more complex scenarios, you can set up notifications between resources:
directory config_dir do
# ... same attributes as before ...
notifies :create, "template[#{config_dir}/database.yml]", :immediately
end
template "#{config_dir}/database.yml" do
# ... same attributes as before ...
action :nothing
end