diff --git a/pyinfra/api/ssh.py b/pyinfra/api/ssh.py
index <HASH>..<HASH> 100644
--- a/pyinfra/api/ssh.py
+++ b/pyinfra/api/ssh.py
@@ -239,6 +239,9 @@ def _get_sftp_connection(state, hostname):
 
 
 def _put_file(state, hostname, file_io, remote_location):
+    # Ensure we're at the start of the file
+    file_io.seek(0)
+
     # Upload it via SFTP
     sftp = _get_sftp_connection(state, hostname)
     sftp.putfo(file_io, remote_location)
diff --git a/pyinfra/api/util.py b/pyinfra/api/util.py
index <HASH>..<HASH> 100644
--- a/pyinfra/api/util.py
+++ b/pyinfra/api/util.py
@@ -184,6 +184,9 @@ def get_file_sha1(filename):
     else:
         file_io = open(filename, 'r')
 
+    # Ensure we're at the start of the file
+    file_io.seek(0)
+
     buff = file_io.read(BLOCKSIZE)
     hasher = sha1()
Seek to start of files before uploading/SHA1-ing.

Problem occurrs on templates where the remote file already exists, we
would get the SHA1 of the StringIO for the template and then upload. But
because SHA1 getting reads the entire file, 0 bytes are left to upload.
So now we just seek(0) before reading files.