<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>emre şahin's digital garden 🍃 - DevOps</title>
    <link>https://emresahin.net/categories/devops/</link>
    <description>Posts in the DevOps category</description>
    <language>en</language>
    <managingEditor>contact@emresahin.net (Emre Şahin)</managingEditor>
    <lastBuildDate>Tue, 15 Sep 2026 19:46:32 +0000</lastBuildDate>
    <atom:link href="https://emresahin.net/categories/devops/rss.xml" rel="self" type="application/rss+xml"/>
    <item>
      <title>Using SSH Private Keys in Dockerfile aimed for Google Cloud Run</title>
      <published>2020-05-12T18:46:51+00:00</published>
      <updated>2020-05-12T18:46:51+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Tue, 12 May 2020 18:46:51 +0000</pubDate>
      <link>https://emresahin.net/using-ssh-private-keys-in-dockerfile-aimed-for-google-cloud-run--24390/</link>
      <guid isPermaLink="true">https://emresahin.net/using-ssh-private-keys-in-dockerfile-aimed-for-google-cloud-run--24390/</guid>
      <description>I had a one-user software application that I had wanted to deploy to Google Cloud Run for some time. It was on Python 3.5, and when I updated the system it lives on, the virtual environment stopped working. It also depended on lxml-3.7 , and that particular version didn’t compile on my new stable...</description>
      <category>DevOps</category>
      <category>Docker</category>
      <category>Cloud Computing</category>
      <category>Docker</category>
      <category>Google Cloud Run</category>
      <category>SSH</category>
      <category>Security</category>
      <category>Python</category>
      <category>Bitbucket</category>
      <content:encoded><![CDATA[<p>I had a one-user software application that I had wanted to deploy to Google Cloud Run for some time. It was on
Python 3.5, and when I updated the system it lives on, the virtual environment stopped working. It
also depended on <code>lxml-3.7</code>, and that particular version didn’t compile on my new stable Debian
installation.</p>
<p>This motivated me to learn Docker and gcloud rather quickly.</p>
<p>I was able to create a new Docker container in a short time. However, as I don’t want to share my Git
SSH key publicly, I needed a way to put an SSH private key (<code>~/.ssh/id_rsa</code>) into this new container
securely, without leaving any trace.</p>
<p>I tried a few things, but in the end, I decided to do something like:</p>
<pre><code class="language-Dockerfile">FROM python:3.5-stretch as intermediate

# add credentials on build
RUN mkdir /root/.ssh/
# To use docker --build-arg, you can uncomment the following two lines and comment out the COPY line below.
# ARG SSH_PRIVATE_KEY
# RUN echo "${SSH_PRIVATE_KEY}" &gt; /root/.ssh/id_rsa
COPY application_sshkey /root/.ssh/id_rsa
RUN chmod 0600 /root/.ssh/id_rsa

# make sure your domain is accepted
RUN touch /root/.ssh/known_hosts
RUN ssh-keyscan bitbucket.org &gt;&gt; /root/.ssh/known_hosts

RUN git clone git@bitbucket.org:username/application /root/application

FROM python:3.5-stretch
COPY --from=intermediate /root/application /root/application

RUN pip3 install -r /root/application/requirements.txt

EXPOSE 9090/tcp

WORKDIR /root/application/

CMD python3 manage.py runserver 0.0.0.0:9090
</code></pre>
<p>Here, <code>application_sshkey</code> is a file I created using <code>ssh-keygen</code> and granted read-only access to on
Bitbucket.</p>
<p>As you can see, the Dockerfile has two <code>FROM</code> statements. It creates a container to clone the
application repository. Then it starts again with a new container and copies the repository to this new
container. This way, it is not possible to peek into the private key using the <code>docker history</code> command.</p>
<p>By the way, I included two methods in the Dockerfile because <code>gcloud builds</code> does not accept a
<code>--build-arg</code> parameter similar to <code>docker build</code>. I’m sure there are other workarounds for passing
secrets to <code>gcloud</code> builds, but instead of digging for them, I found a solution that works for both
Docker and Google Cloud Run.</p>]]></content:encoded>
    </item>
    <item>
      <title>SSH Keys for Multiple Accounts on GitHub</title>
      <published>2018-11-18T16:18:58+00:00</published>
      <updated>2018-11-18T16:18:58+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sun, 18 Nov 2018 16:18:58 +0000</pubDate>
      <link>https://emresahin.net/ssh-keys-multiple-accounts-14371-8399/</link>
      <guid isPermaLink="true">https://emresahin.net/ssh-keys-multiple-accounts-14371-8399/</guid>
      <description>I have multiple GitHub accounts, and some of these are collaborators on others. I don’t like to enter my password every time I push, so I set up SSH keys for my accounts. However, GitHub (understandably) doesn’t accept the same key for more than one account. (Otherwise, how would it know which ac...</description>
      <category>DevOps</category>
      <category>Tutorial</category>
      <category>SSH</category>
      <category>GitHub</category>
      <category>Git</category>
      <category>Config</category>
      <content:encoded><![CDATA[<p>I have multiple GitHub accounts, and some of these are collaborators on others. I
don’t like to enter my password every time I push, so I set up SSH keys for my
accounts. However, GitHub (understandably) doesn’t accept the same key for more than one
account. (Otherwise, how would it know which account is being used?)</p>
<p>Fortunately, there is a way to use the <code>~/.ssh/config</code> file to specify different keys for different
target URLs.</p>
<pre><code class="language-config"># Personal account - the default config
Host github.com
   HostName github.com
   User git
   IdentityFile ~/.ssh/id_rsa

# Work Account
Host work.github.com
   HostName github.com
   User git
   IdentityFile ~/.ssh/id_rsa_work
</code></pre>
<p>Now you can create another SSH key in <code>~/.ssh/id_rsa_work</code> and add it to your
work account. When you configure a repository, you need to specify
<code>git@work.github.com:user/repo.git</code> as the repository address rather than
<code>git@github.com:user/repo.git</code>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Fixing Pip Timeout Problems</title>
      <published>2018-11-16T18:58:31+00:00</published>
      <updated>2018-11-16T18:58:31+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Fri, 16 Nov 2018 18:58:31 +0000</pubDate>
      <link>https://emresahin.net/pip-default-timeout-14371-8665/</link>
      <guid isPermaLink="true">https://emresahin.net/pip-default-timeout-14371-8665/</guid>
      <description>When installing a large package like TensorFlow , I encountered the following error in pip : pip._vendor.urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host='files.pythonhosted.org', port=443): Read timed out. v = self._sslobj.read(len, buffer) socket.timeout: The read operation timed o...</description>
      <category>Python</category>
      <category>DevOps</category>
      <category>pip</category>
      <category>TensorFlow</category>
      <category>Troubleshooting</category>
      <content:encoded><![CDATA[<p>When installing a large package like <em>TensorFlow</em>, I encountered the following error in <code>pip</code>:</p>
<pre><code class="language-text">pip._vendor.urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host='files.pythonhosted.org', port=443): Read timed out.

    v = self._sslobj.read(len, buffer)
socket.timeout: The read operation timed out
</code></pre>
<p>I noticed that <code>pip</code> has a <code>--default-timeout</code> parameter that can be configured to avoid this issue:</p>
<pre><code class="language-bash">pip --default-timeout=1000 install package-name
</code></pre>]]></content:encoded>
    </item>
    <item>
      <title>Backup Script for Recent Files</title>
      <published>2014-02-01T22:00:00+00:00</published>
      <updated>2014-02-01T22:00:00+00:00</updated>
      <author>Emre Şahin</author>
      <pubDate>Sat, 01 Feb 2014 22:00:00 +0000</pubDate>
      <link>https://emresahin.net/backup-script-for-recent-files/</link>
      <guid isPermaLink="true">https://emresahin.net/backup-script-for-recent-files/</guid>
      <description>I decided to write a script to back up only recent files. There are solutions based on unison that work periodically for all files, but as I change projects, I need to configure new backups for these projects as well. This is cumbersome and error-prone; it is easy to forget to add new artifacts t...</description>
      <category>cli</category>
      <category>tools</category>
      <category>devops</category>
      <category>backup</category>
      <category>rsync</category>
      <category>unison</category>
      <category>bash</category>
      <category>script</category>
      <category>cron</category>
      <category>linux</category>
      <category>automation</category>
      <content:encoded><![CDATA[<p>I decided to write a script to back up only recent files. There are solutions based on <a href="http://www.cis.upenn.edu/~bcpierce/unison/">unison</a> that work periodically for all files, but as I change projects, I need to configure new backups for these projects as well. This is cumbersome and error-prone; it is easy to forget to add new artifacts to backup scripts and lose them in an emergency.</p>
<p>Therefore, I decided that a small Bash script using <a href="http://rsync.samba.org/">rsync</a> and <a href="http://en.wikipedia.org/wiki/Find">find</a> would work better. It monitors my entire home directory and backs up recent files.</p>
<p>The following script does exactly that:</p>
<pre><code class="language-bash">#!/bin/bash

if [ "x$1" = "x" ] ; then
    TARGET=/media/augustus/backup-recent-`hostname`
else
    TARGET=$1
fi

PERIOD=15

mkdir -p $TARGET

# Delete files older than $PERIOD days
find $TARGET -ctime +$PERIOD -print -delete

# Copy files newer than $PERIOD under ~. Ignore files under .hg
for d in ~/*/ ; do
    find $d -path '*/.hg/*' -prune -o -type f -ctime -$PERIOD -print  -exec rsync -aRv {} $TARGET/ \;
done
</code></pre>
<p>It checks whether a command-line option is provided as the target; otherwise, it sets a default target. <code>/media/augustus/</code> is an NFS mount in my case, but you can specify any path.</p>
<p><code>PERIOD</code> is the number of days that the script considers <em>recent</em>. Currently, it backs up files changed in the last 15 days.</p>
<p>The script deletes older files from the backup. Since I use other solutions for long-term storage, I don’t want to keep them here, especially since this script runs via <code>cron</code> every two hours.</p>
<p>Note that the script only checks directories under the home directory, so files located directly in the home directory are not backed up.</p>
<p>It also skips files under <code>.hg</code> directories. You can add more <code>-prune</code> options to the <code>find</code> command to exclude other irrelevant directories from your backup.</p>]]></content:encoded>
    </item>
  </channel>
</rss>
