<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://ebobby.org/feed.xml" rel="self" type="application/atom+xml" /><link href="https://ebobby.org/" rel="alternate" type="text/html" /><updated>2026-08-13T13:36:54-07:00</updated><id>https://ebobby.org/feed.xml</id><title type="html">Francisco Soto</title><author><name>Francisco Soto</name></author><entry><title type="html">Every mistake in the book.</title><link href="https://ebobby.org/2024/01/07/Every-mistake-in-the-book/" rel="alternate" type="text/html" title="Every mistake in the book." /><published>2024-01-07T00:00:00-07:00</published><updated>2024-01-08T09:37:36-07:00</updated><id>https://ebobby.org/2024/01/07/Every-mistake-in-the-book</id><content type="html" xml:base="https://ebobby.org/2024/01/07/Every-mistake-in-the-book/"><![CDATA[<p>I started building <strong>Pick Your Photos</strong> since June last year. I didn’t really
focus too much into it at the beginning, it was just a way to pass time and play
with newer versions of Ruby on Rails.</p>

<p>Sometimes I had bursts of intense coding as I got hooked by a specific feature
that I wanted to build. Trying to display a gallery of photos on which every
photo displayed keeps it’s aspect ratio, it’s lazy loaded, works nicely on
mobile is quite the feat.</p>

<p>These last few weeks though I have been pouring into it almost a 100% of my free
time.</p>

<p>I didn’t really want to release something half baked, or that looked bad, given
that my intended market is professional photographers and they are really
aesthetic/visual beauty oriented.</p>

<blockquote>
  <p>“If You’re Not Embarrassed By The First Version Of Your Product, You’ve Launched Too Late”</p>
</blockquote>

<p>I’ve talked with some of photographers and I, more or less, understand what
they’re looking for. But likely I haven’t talked with enough of them. I know
they’re out there but I am not entirely sure I know where I can reach them in
order to market to them.</p>

<blockquote>
  <p>“Spend more time marketing than building.”</p>
</blockquote>

<p>I follow a lot of indie hackers and entrepreneurs on Twitter and most of them
start first with some marketing pages and then, when they are sure, they start
building. I already integrated payments, have a VPS and have the product running
(not officially launched yet, only my wife uses it) and I haven’t yet built the
landing page.</p>

<blockquote>
  <p>“Build a landing page first.”</p>
</blockquote>

<p>I am in like 6 months, on and off, into this. I really like what I have right
now. It looks good and offers the basic functionality they need for client
galleries, selection and delivery. But, again, the people I follow usually talk
a lot about how they launched several products in a month, and how they intend
to launch, say, 20 products this year. And although I understand where that’s
coming from I am not really sure I, personally, would love to work like that.</p>

<blockquote>
  <p>“Fail fast.”</p>
</blockquote>

<p>I am very likely making more mistakes. These are the ones I could think of the
top of my head, but the ones that get you are the ones you don’t know about. And
from now on the journey is going to get more interesting as I am almost ready
for the “launch”.</p>

<p>At least with some photographers I know and I can reach out to directly.</p>

<p>So, more mistakes to come soon…</p>

<p><small>
  Originally posted on <a target="_blank" href="https://www.indiehackers.com/product/pick-your-photos/probably-making-every-mistake-in-the-book--Nn_vai5EGoqHwcQCYko">Indie Hackers</a>.
</small></p>]]></content><author><name>Francisco Soto</name></author><summary type="html"><![CDATA[I started building Pick Your Photos since June last year. I didn’t really focus too much into it at the beginning, it was just a way to pass time and play with newer versions of Ruby on Rails.]]></summary></entry><entry><title type="html">My Entrepreneurship Journey</title><link href="https://ebobby.org/2023/07/20/My-entrepreneurship-journey/" rel="alternate" type="text/html" title="My Entrepreneurship Journey" /><published>2023-07-20T00:00:00-07:00</published><updated>2023-07-20T19:02:02-07:00</updated><id>https://ebobby.org/2023/07/20/My-entrepreneurship-journey</id><content type="html" xml:base="https://ebobby.org/2023/07/20/My-entrepreneurship-journey/"><![CDATA[<p>I suck at entrepreneurship. At least, that’s how I sometimes feel.</p>

<p>Ever since I was a teenager, who recently learned programming, I wanted to build
a company. Fueled by the myth of the software companies of the 80s and 90s I
dreamed of being the next Microsoft or ID Software. A bunch of smart hackers
building something massive and then riding off into the sunset on red Ferraris.</p>

<p>Being a penniless Mexican kid though, far from Silicon Valley, made this dream a
bit too far to reach. When I got out of school I realized that having no money
and zero connections would be a rough start, so I joined the work force as a
programmer, it seemed that learning about how businesses worked from the inside,
while making money, was the only path to follow.</p>

<h2 id="hello-code--chopeo">Hello Code / Chopeo</h2>

<p>Fast forward a few years and I was living in San Francisco and working for a cool
startup, but, I had the nagging thought in the back of my head about starting
and building something up.</p>

<p>So, I decided to do something. I quit my job, and in what was a weird move, I
came back to Mexico to start something up.</p>

<p>My first thought was to do something on my own, I had savings and could work on
my own for a while. I wanted to build a small product that I could build and
support myself. I had no idea what, or how, but that’s what I wanted!</p>

<p>On my way back though, I promised a friend I would take a meeting with his
brother who wanted to invest on a technology business, that didn’t really fit my
plans but I wanted to be polite and took the meeting, and, as these things go, I
did accept and decided to build whatever I was going to build with some
investment and as a part of a larger group of companies (not tech).</p>

<p>We built <a href="https://web.archive.org/web/20141219064754/https://www.chopeo.mx/">Chopeo</a>. A build-your-own-ecommerce application tailored for
the Mexican market. At the time Shopify was making a big deal of entering the
Mexican market, and there were other larger incumbents, like Kichink, but we
thought we could carve a decent niche for ourselves, sadly though, small niches
aren’t that great when you have investment pressure.</p>

<p>We probably made every mistake in the book. Not validating the market, not
setting up good channels for customer feedback, we didn’t try multiple marketing
channels and focused in the ones we knew and we probably did it wrong.</p>

<p>But, I enjoyed the experience a lot. We built something that users loved. I did
a lot of customer support because I enjoyed connecting with our users. I loved
watching their reactions when we fixed a bug they reported, or when we released
a feature they asked for hours later (sometimes minutes!). It was just magical.</p>

<p>But, alas, we failed. We closed down.</p>

<p>We got lucky though, and another startup that was building a point of sales app
wanted our technology to offer ecommerce as part of their whole suite. We sold
it for a “whooping” 120K USD.</p>

<h2 id="kraken-gastropub">Kraken Gastropub</h2>

<p>A couple of years later a friend came to my house and said something to the
lines of “let’s open a bar”. At the time I was deep in the culture of craft beer
and food, and even though I knew it is a difficult business and we would
probably fail, I said yes.</p>

<p>So we opened <a href="https://goo.gl/maps/kHVDnCK7UhC26Uq9A">Kraken Gastropub</a>. We had a clear vision of what we
wanted. The type of place we loved to eat, drink and could only find while
traveling. A place with bar food (burger, hotdog) but, high quality. A great
lineup of craft beers. Music to our taste. People left sometimes because they
wanted to change the music. Or because we chose not to display sports on our TVs
most of the time. When people asked for a beer we could try to share the culture
of craft beer, what it was, different styles, what type of flavors, what type of
food to pair it with, etc.</p>

<p>We were uncompromising. We knew that if we made something we actually liked and
not compromised on our vision people with similar taste would love it (and
people with different taste would hate it).</p>

<p>We made many, many mistakes as well. Our location was bad, it had zero foot
traffic. Most people that walked into our restaurant knew they were coming
before they left their house. We had large operating costs. Rent, premium
ingredients, a large staff. We didn’t know how to market it or where.</p>

<p>But, after two grueling years we started seeing some black ink in our
balances. Slowly but steadily we grew. Word of mouth was great. We had a lot of
loyal regulars. A lot of our customers would come with us when they had
important events, graduations, valentine’s, etc.</p>

<p>And then the COVID pandemic hit. And with the uncertainty of what would happen, how
long it would take, and our high operating costs, we had to shut down. You can
still see our “we’ll be coming back soon” post in <a href="https://www.instagram.com/krakenhmo/">Instagram</a>.</p>

<p>And that was it.</p>

<h2 id="and-now">And now</h2>

<p>What would I do if I had 100M USD right now? That’s a good thought exercise when
trying to figure out what is it that you truly want to do.</p>

<p>I’ve thought long and hard about what I would like to build and how. I really
like developing products and have a close relationship with customers. I love
building <em>high</em> quality products. With close attention to detail. Something that
is a pleasure to interact with. To use. To be in. I’ve decided to give
solopreneurship/indie hacking a try.</p>

<p>I am building now a small web application for photographers. A tool for picture
proofing that allow photographers clients  to review and approve their photos. I
got the idea for this from my wife. She’s a photographer and she has complained
multiple times about the tool she has used before. Why not build something that
could give her a helping hand? If it eases her workflow and improves her
service, chances are, it could do the same for other photographers out there.</p>

<p>Did I vet the market? Build a few landing pages to decide which product to build?
Did I interview many potential users? (this one I actually did, but low volume,
just a few photographers I know, plus my wife). I did not.</p>

<p>I know there is a market. But I don’t know the size. There are some competitors
out there. I know a bunch of photographers (through my wife). And to be honest I
don’t intend to build a big company. If I get enough users to make it worth
working on and build something good, I’m down.</p>

<p>I am also taking the opportunity to build it entirely on my own so I can
exercise some of my weak skills, like HTML and CSS. Also I am using this to
play with some technologies I wanted to play with (HTML over the wire).</p>

<p>I am not trying to build a perfect product, but I am definitely paying a lot of
attention to quality. I am trying to keep it as small in terms of features
as possible so I can build them to a standard that satisfies me.</p>

<p>Doing that takes more time? Yes, it does. And, taking long to build something
just to fail is the mistake we developers commit a lot. I understand all that.</p>

<p>Will it fail? Probably. Hopefully I will learn some lessons for the next time.</p>

<h2 id="about-indie-hacking">About indie hacking</h2>

<p>I have a lot of internal conflict between my own personal goals and “the way
you’re supposed to do it” when I consume the indie hacking content out there.</p>

<p>Much of the Indie Hacking content out there, especially on Twitter, seems to
promote rapid production— the more products, the better. The notion is that the
quicker you produce, the faster you’ll hit your market.</p>

<p>It makes sense, right? A lot of products fail not due to their quality, but
because they couldn’t find a market or because they couldn’t reach that market.
So, the more you produce in the shortest amount of time, the faster you find the
one that will be successful.</p>

<p>But, when I think about that, you must have to make a lot of compromises, right?
Like building your whole app in a <a href="https://twitter.com/levelsio/status/1308145873843560449">single file</a>. I don’t think that’s
wrong at all, it suits their purposes and they are evidently doing great.</p>

<p>But I am not sure that fits my goals. In a hypothetical world if I build a
product that actually has a modicum of success I don’t want to deal with
something like that. My “dream” would be to have a great base to keep building a
great product. With attention to detail. A well crafted product.</p>

<p>The argument could be made that once you have a product with traction you can
actually improve it and make it better. And it’s a good point. But I really want
to enjoy the journey, not just the end result.</p>

<p>I guess I will find out!</p>]]></content><author><name>Francisco Soto</name></author><summary type="html"><![CDATA[I suck at entrepreneurship. At least, that’s how I sometimes feel.]]></summary></entry><entry><title type="html">Remote Work: Location based compensation</title><link href="https://ebobby.org/2023/06/12/Remote-work-Location-based-compensation/" rel="alternate" type="text/html" title="Remote Work: Location based compensation" /><published>2023-06-12T00:00:00-07:00</published><updated>2023-06-12T12:43:08-07:00</updated><id>https://ebobby.org/2023/06/12/Remote-work-Location-based-compensation</id><content type="html" xml:base="https://ebobby.org/2023/06/12/Remote-work-Location-based-compensation/"><![CDATA[<h2 id="the-evolution-of-work">The Evolution of Work</h2>

<p>Once upon a time, location was not a factor in finding a job. You worked where
you were. We didn’t even think about any other possibility. Your physical
location limited your job prospects and your earning potential.</p>

<p>Living and working as a software engineer in Mexico, working for a great
technology company was merely a dream. And the dream involved relocating. Fast
forward a couple of decades later and the dream has become reality, but with a
twist. Remote work is the norm, not the exception.</p>

<p>As an industry and society though, we are still adapting to the new
reality. There are a lot of arguments in favor of remote work, many against. And
companies and employees are figuring out how to make it work. What is the best
setup, processes, philosophies, techniques, etc.</p>

<p>One of such is <em>location-based compensation adjustments</em>.</p>

<h2 id="location-based-compensation-adjustments">Location based compensation adjustments</h2>

<p>A few days ago I received an intriguing job offer. The role was interesting, the
company well known in the startup world. But, there was a hitch. They offered
less than the advertised range. They applied a 20% location-based adjustment to
the offered compensation because of where I live. Despite the job being remote,
they considered my physical location a factor to apply to my compensation.</p>

<p>I’ve seen many companies that do this, I usually ignore those job postings but
in this instance I missed the fine print.</p>

<p>I often question the logic behind this. In the past, my sole focus was to be a
master at my craft. I invested years to build and improve my skills, to
understand the nuances of software development. Now, my geographic location,
irrelevant to my ability as a developer, was a deciding factor in my
compensation.</p>

<h2 id="the-companys-perspective">The Company’s Perspective</h2>

<p>Location-based compensation adjustments are often pitched as a means of
fairness. The argument is that since the cost of living varies significantly
from one region to another, it is equitable that pay scales are adjusted
accordingly.</p>

<p>Does it? Does it truly promote fairness?</p>

<p>Is it really fair to pay an employee less money for the same work just because
of where they happen to live? Isn’t it a bit <em>too much</em> that the company is
taking it upon itself to police which standard of living their employees should
have based on their role and position?</p>

<p>Could it be that it just happens to save some money?</p>

<p>I understand companies need to maintain profitability. I don’t take it personal
when companies do this, or when they offer below market base compensation
because that’s all they can afford. It is my decision if I want to work for that
company based on other factors.  But we should call things what they are.</p>

<h2 id="turning-down-the-offer">Turning Down the Offer</h2>

<p>In the end I chose to decline the offer. It simply didn’t sit right with me. I
just knew that this was going to sit in the back of my mind and be a constant
reminder that the work that I would do was considered to be -20% less valuable,
regardless of its own merits. It’s not about the money, but the principle.</p>

<p>As I said, I didn’t burn any bridges. I had a very lighthearted conversation
with the Hiring Manager. I understand that’s what they do, and it’s up to me if
that is how I want to work, and decided it wasn’t.</p>

<h2 id="final-thoughts">Final Thoughts</h2>

<p>So here we are, at the frontier of a new era, where you can do work from
anywhere in the world. As we navigate this blurred landscape we should try very
hard not to bring the old paradigms with us. We should shed the old, outdated
practices and develop new ones. It’s time we moved away from arbitrary factors
like location and focused more on the skills and the value we bring to the
table.</p>

<p>A the end of the day, it’s the human behind the computer that counts, not where
they happen to live.</p>

<p>Let’s create a future where fair compensation isn’t determined by a map, but by
the merit of our work.</p>]]></content><author><name>Francisco Soto</name></author><summary type="html"><![CDATA[The Evolution of Work]]></summary></entry><entry><title type="html">The words we say and the things we do…</title><link href="https://ebobby.org/2023/04/28/the-words-we-say-and-the-things-we-do/" rel="alternate" type="text/html" title="The words we say and the things we do…" /><published>2023-04-28T00:00:00-07:00</published><updated>2023-06-05T22:58:27-07:00</updated><id>https://ebobby.org/2023/04/28/the-words-we-say-and-the-things-we-do</id><content type="html" xml:base="https://ebobby.org/2023/04/28/the-words-we-say-and-the-things-we-do/"><![CDATA[<p>When we interact with people that are close to us, emotionally or physically, we
have the potential to have a big impact on people’s lives in ways that we may
not even imagine.</p>

<p>Back in high school some guy decided I had a “Bobby” face and started referring
to me as such. For context, my real name is Francisco so there’s no way to make
a connection to Bobby.</p>

<p>In time everyone in high school started calling me Bobby because they heard it
from him. Some even did it because they knew I wasn’t very happy to be called
something that was not my name.</p>

<p>Eventually I came to the conclusion that I should just leave things the way they
were. Mexicans tend to be very, very mean with nicknames and, as far as
nicknames go, Bobby was harmless. In order to avoid being assigned a new,
meaner, nickname I started to go with the flow.</p>

<p>And here I am, a couple of decades later, and my website, email, every handle in
every website is Bobby. My wife, my friends, my family, <em>everyone</em> calls me
Bobby. Sometimes, even old acquaintances call me “Roberto” and are befuddled
when they realize it isn’t my actual name.</p>

<p>That guy had a massive influence in my life. He renamed me, superseding my
parents. The funny thing is that he didn’t stay very long in that high
school. He was there for about 6 months and left. I can’t even remember his name
but the impact of his joke is still with me and will stay with me till I die. I
am not mad or anything like that, today the name Bobby feels like a larger part
of my identity than even my name. When I remember how I came to be I chuckle and
think that it’s very funny.</p>

<p>But at some level it also helps me be aware that even the smallest of things, a
simple action or a casual remark, can have a huge, long lasting impact on
somebody’s life. It helps me try to keep it positive. If they are not going to
remember my name but remember what I do or say, what do I want that to be?</p>

<p>Keep it positive. Be kind to other people and remember what they did or said
kindly. Sometimes we do not understand the power we have. Wield it wisely.</p>]]></content><author><name>Francisco Soto</name></author><summary type="html"><![CDATA[When we interact with people that are close to us, emotionally or physically, we have the potential to have a big impact on people’s lives in ways that we may not even imagine.]]></summary></entry><entry><title type="html">vagrant-lxc / pipework / lxc</title><link href="https://ebobby.org/2019/01/21/debugging-series-vagrant-lxc-pipework-lxc/" rel="alternate" type="text/html" title="vagrant-lxc / pipework / lxc" /><published>2019-01-21T00:00:00-07:00</published><updated>2023-06-05T22:58:27-07:00</updated><id>https://ebobby.org/2019/01/21/debugging-series-vagrant-lxc-pipework-lxc</id><content type="html" xml:base="https://ebobby.org/2019/01/21/debugging-series-vagrant-lxc-pipework-lxc/"><![CDATA[<p>I personally consider “debugging” (figuring out how something works and how is
it broken) a very important skill as a software developer and one that I don’t
think it is taught explicitly. It is simply left for us to figure it out.</p>

<p>For this reason I wanted to write about it for a while. Specially with examples
of real life problems and debugging sessions as I find they are far more useful
that simple made up examples.</p>

<p>This is the first in what likely will be a long series of posts as software
breaks all the time…</p>

<h2 id="software-breaks-all-the-time">Software breaks all the time…</h2>

<p>I’ve been using vagrant and virtualbox to setup my development environments for
a few years now. It’s a pretty convenient way to create reproducible
environments. Since I moved to linux though I’ve been using <a href="https://github.com/fgrehm/vagrant-lxc">vagrant-lxc</a> instead
of virtualbox as cointainers are just as isolated as virtual machines and they
are significantly faster.</p>

<p>Recently though, this stopped working as I tried to set up a new dev env. It
simply errored out.</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="gp">$</span><span class="w"> </span>vagrant up
<span class="go">Bringing machine 'default' up with 'lxc' provider...
</span><span class="gp">==&gt;</span><span class="w"> </span>default: Importing base box <span class="s1">'emptybox/ubuntu-bionic-amd64-lxc'</span>...
<span class="gp">==&gt;</span><span class="w"> </span>default: Checking <span class="k">if </span>box <span class="s1">'emptybox/ubuntu-bionic-amd64-lxc'</span> version <span class="s1">'0.1.1546433870'</span> is up to date...
<span class="gp">==&gt;</span><span class="w"> </span>default: Setting up mount entries <span class="k">for </span>shared folders...
<span class="gp">    default: /vagrant =&gt;</span><span class="w"> </span>/home/fms/Projects/local-devenv
<span class="gp">==&gt;</span><span class="w"> </span>default: Starting container...
<span class="gp">==&gt;</span><span class="w"> </span>default: Waiting <span class="k">for </span>machine to boot. This may take a few minutes...
<span class="go">    default: SSH address: 10.0.3.169:22
    default: SSH username: vagrant
    default: SSH auth method: private key
    default:
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default:
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
</span><span class="gp">==&gt;</span><span class="w"> </span>default: Machine booted and ready!
<span class="gp">==&gt;</span><span class="w"> </span>default: Setting up private networks...
<span class="go">There was an error executing ["sudo", "/usr/local/bin/vagrant-lxc-wrapper", "/home/fms/.vagrant.d/gems/2.6.0/gems/vagrant-lxc-1.4.3/scripts/pipework", "vlxcbr1", "devenv", "192.168.64.4/24"]

For more information on the failure, enable detailed logging by setting
the environment variable VAGRANT_LOG to DEBUG.</span></code></pre></figure>

<p>Yeeey… fun.</p>

<h2 id="investigation">Investigation</h2>

<p>Apparently when we try to create our container, vagrant is failing to run a
command while trying to set up private networks. We have the command that it’s
trying to run but we don’t have any error output. Let’s try vagrant suggestion
and check the debug log.</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="gp">$</span><span class="w"> </span><span class="nv">VAGRANT_LOG</span><span class="o">=</span>debug vagrant up
<span class="go">.... lots of output ....
</span><span class="gp">==&gt;</span><span class="w"> </span>default: Setting up private networks...
<span class="go"> INFO driver: Configuring network interface for devenv using 192.168.64.4 and bridge vlxcbr1
 INFO driver: Checking whether bridge vlxcbr1 exists
 INFO driver: Creating the bridge vlxcbr1
 INFO subprocess: Starting process: ["/usr/bin/sudo", "/usr/local/bin/vagrant-lxc-wrapper", "brctl", "addbr", "vlxcbr1"]
 INFO subprocess: Command not in installer, restoring original environment...
DEBUG subprocess: Selecting on IO
DEBUG subprocess: Waiting for process to exit. Remaining to timeout: 32000
DEBUG subprocess: Exit status: 0
 INFO driver: Checking whether the bridge vlxcbr1 has an IP
 INFO driver: Adding 192.168.64.254 to the bridge vlxcbr1
 INFO subprocess: Starting process: ["/usr/bin/sudo", "/usr/local/bin/vagrant-lxc-wrapper", "ip", "addr", "add", "192.168.64.254/24", "dev", "vlxcbr1"]
 INFO subprocess: Command not in installer, restoring original environment...
DEBUG subprocess: Selecting on IO
DEBUG subprocess: Waiting for process to exit. Remaining to timeout: 32000
DEBUG subprocess: Exit status: 0
 INFO subprocess: Starting process: ["/usr/bin/sudo", "/usr/local/bin/vagrant-lxc-wrapper", "ip", "link", "set", "vlxcbr1", "up"]
 INFO subprocess: Command not in installer, restoring original environment...
DEBUG subprocess: Selecting on IO
DEBUG subprocess: Waiting for process to exit. Remaining to timeout: 32000
DEBUG subprocess: Exit status: 0
 INFO subprocess: Starting process: ["/usr/bin/sudo", "/usr/local/bin/vagrant-lxc-wrapper", "/home/fms/.vagrant.d/gems/2.6.0/gems/vagrant-lxc-1.4.3/scripts/pipework", "vlxcbr1", "devenv", "192.168.64.4/24"]
 INFO subprocess: Command not in installer, restoring original environment...
DEBUG subprocess: Selecting on IO
DEBnUG subprocess: stderr: Found more than one container matching devenv.
DEBUG subprocess: Waiting for process to exit. Remaining to timeout: 32000
DEBUG subprocess: Exit status: 1
ERROR warden: Error occurred: There was an error executing ["sudo", "/usr/local/bin/vagrant-lxc-wrapper", "/home/fms/.vagrant.d/gems/2.6.0/gems/vagrant-lxc-1.4.3/scripts/pipework", "vlxcbr1", "devenv", "192.168.64.4/24"]

For more information on the failure, enable detailed logging by setting
the environment variable VAGRANT_LOG to DEBUG.
 INFO warden: Beginning recovery process...

 .... more output ....</span></code></pre></figure>

<p>We don’t get much additional detail from that. So my next step is simply to try
to run the command by hand and see if I can catch anything.</p>

<p>(EDIT: It was pointed out to me that the error I was looking for is indeed
present in the debug log and I missed it.)</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="gp">$</span><span class="w"> </span><span class="nb">sudo</span> /usr/local/bin/vagrant-lxc-wrapper /home/fms/.vagrant.d/gems/2.6.0/gems/vagrant-lxc-1.4.3/scripts/pipework vlxcbr1 devenv 192.168.64.4/24
<span class="go">Found more than one container matching devenv.
</span><span class="gp">$</span></code></pre></figure>

<p>A clue! pipework seems to think we have two containers with the same name.</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="gp">$</span><span class="w"> </span><span class="nb">sudo </span>lxc-ls
<span class="go">devenv
</span><span class="gp">$</span><span class="w"> </span><span class="nb">sudo ls</span> /var/lib/lxc
<span class="go">devenv</span></code></pre></figure>

<p>Well. We don’t. What is going on? Let’s see if we can figure out what pipework
is trying to do. Thankfully, it is a bash script so we can simply open it in our
favorite text editor and search for “more than one”, which brings us to this
piece of code:</p>

<p>(This code is included in vagrant-lxc 1.4.3, under scripts/pipework and starting in line 146.)</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c"># Second step: find the guest (for now, we only support LXC containers)</span>
<span class="k">while </span><span class="nb">read </span>_ mnt fstype options _<span class="p">;</span> <span class="k">do</span>
  <span class="o">[</span> <span class="s2">"</span><span class="nv">$fstype</span><span class="s2">"</span> <span class="o">!=</span> <span class="s2">"cgroup"</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="k">continue
  </span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$options</span><span class="s2">"</span> | <span class="nb">grep</span> <span class="nt">-qw</span> devices <span class="o">||</span> <span class="k">continue
  </span><span class="nv">CGROUPMNT</span><span class="o">=</span><span class="nv">$mnt</span>
<span class="k">done</span> &lt; /proc/mounts

<span class="o">[</span> <span class="s2">"</span><span class="nv">$CGROUPMNT</span><span class="s2">"</span> <span class="o">]</span> <span class="o">||</span> <span class="o">{</span>
    die 1 <span class="s2">"Could not locate cgroup mount point."</span>
<span class="o">}</span>

<span class="c"># Try to find a cgroup matching exactly the provided name.</span>
<span class="nv">N</span><span class="o">=</span><span class="si">$(</span>find <span class="s2">"</span><span class="nv">$CGROUPMNT</span><span class="s2">"</span> <span class="nt">-name</span> <span class="s2">"</span><span class="nv">$GUESTNAME</span><span class="s2">"</span> | <span class="nb">wc</span> <span class="nt">-l</span><span class="si">)</span>
<span class="k">case</span> <span class="s2">"</span><span class="nv">$N</span><span class="s2">"</span> <span class="k">in
  </span>0<span class="p">)</span>
    <span class="c"># If we didn't find anything, try to lookup the container with Docker.</span>
    <span class="k">if </span>installed docker<span class="p">;</span> <span class="k">then
      </span><span class="nv">RETRIES</span><span class="o">=</span>3
      <span class="k">while</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$RETRIES</span><span class="s2">"</span> <span class="nt">-gt</span> 0 <span class="o">]</span><span class="p">;</span> <span class="k">do
        </span><span class="nv">DOCKERPID</span><span class="o">=</span><span class="si">$(</span>docker inspect <span class="nt">--format</span><span class="o">=</span><span class="s1">'{{ .State.Pid }}'</span> <span class="s2">"</span><span class="nv">$GUESTNAME</span><span class="s2">"</span><span class="si">)</span>
        <span class="o">[</span> <span class="s2">"</span><span class="nv">$DOCKERPID</span><span class="s2">"</span> <span class="o">!=</span> 0 <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nb">break
        sleep </span>1
        <span class="nv">RETRIES</span><span class="o">=</span><span class="k">$((</span>RETRIES <span class="o">-</span> <span class="m">1</span><span class="k">))</span>
      <span class="k">done</span>

      <span class="o">[</span> <span class="s2">"</span><span class="nv">$DOCKERPID</span><span class="s2">"</span> <span class="o">=</span> 0 <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="o">{</span>
        die 1 <span class="s2">"Docker inspect returned invalid PID 0"</span>
      <span class="o">}</span>

      <span class="o">[</span> <span class="s2">"</span><span class="nv">$DOCKERPID</span><span class="s2">"</span> <span class="o">=</span> <span class="s2">"&lt;no value&gt;"</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="o">{</span>
        die 1 <span class="s2">"Container </span><span class="nv">$GUESTNAME</span><span class="s2"> not found, and unknown to Docker."</span>
      <span class="o">}</span>
    <span class="k">else
      </span>die 1 <span class="s2">"Container </span><span class="nv">$GUESTNAME</span><span class="s2"> not found, and Docker not installed."</span>
    <span class="k">fi</span>
    <span class="p">;;</span>
  1<span class="p">)</span> <span class="nb">true</span> <span class="p">;;</span>
  <span class="k">*</span><span class="p">)</span> die 1 <span class="s2">"Found more than one container matching </span><span class="nv">$GUESTNAME</span><span class="s2">."</span> <span class="p">;;</span>
<span class="k">esac</span></code></pre></figure>

<p>It may look complicated if you haven’t done shell scripting before, but if you
look at it closely it is relatively easy to find out what it is doing. Let’s
dissect it.</p>

<p>In the line:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="nv">N</span><span class="o">=</span><span class="si">$(</span>find <span class="s2">"</span><span class="nv">$CGROUPMNT</span><span class="s2">"</span> <span class="nt">-name</span> <span class="s2">"</span><span class="nv">$GUESTNAME</span><span class="s2">"</span> | <span class="nb">wc</span> <span class="nt">-l</span><span class="si">)</span></code></pre></figure>

<p>It’s trying to find something in the path <code class="language-plaintext highlighter-rouge">$CGROUPMNT</code> that is named after our
container “devenv” and if it finds more than one it returns the error we
got. What is <code class="language-plaintext highlighter-rouge">$CGROUPMNT</code>?</p>

<p>We find that here:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="k">while </span><span class="nb">read </span>_ mnt fstype options _<span class="p">;</span> <span class="k">do</span>
  <span class="o">[</span> <span class="s2">"</span><span class="nv">$fstype</span><span class="s2">"</span> <span class="o">!=</span> <span class="s2">"cgroup"</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="k">continue
  </span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$options</span><span class="s2">"</span> | <span class="nb">grep</span> <span class="nt">-qw</span> devices <span class="o">||</span> <span class="k">continue
  </span><span class="nv">CGROUPMNT</span><span class="o">=</span><span class="nv">$mnt</span>
<span class="k">done</span> &lt; /proc/mounts</code></pre></figure>

<p>It’s iterating the contents of <code class="language-plaintext highlighter-rouge">/proc/mounts</code> and searching for anything that has
cgroup as file system and devices in the path.</p>

<p>Let’s try that out:</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="gp">$</span><span class="w"> </span><span class="nb">cat</span> /proc/mounts | <span class="nb">grep </span>cgroup
<span class="go">tmpfs /sys/fs/cgroup tmpfs ro,nosuid,nodev,noexec,mode=755 0 0
cgroup2 /sys/fs/cgroup/unified cgroup2 rw,nosuid,nodev,noexec,relatime,nsdelegate 0 0
cgroup /sys/fs/cgroup/systemd cgroup rw,nosuid,nodev,noexec,relatime,xattr,name=systemd 0 0
cgroup /sys/fs/cgroup/blkio cgroup rw,nosuid,nodev,noexec,relatime,blkio 0 0
cgroup /sys/fs/cgroup/net_cls,net_prio cgroup rw,nosuid,nodev,noexec,relatime,net_cls,net_prio 0 0
cgroup /sys/fs/cgroup/cpu,cpuacct cgroup rw,nosuid,nodev,noexec,relatime,cpu,cpuacct 0 0
cgroup /sys/fs/cgroup/hugetlb cgroup rw,nosuid,nodev,noexec,relatime,hugetlb 0 0
cgroup /sys/fs/cgroup/rdma cgroup rw,nosuid,nodev,noexec,relatime,rdma 0 0
cgroup /sys/fs/cgroup/memory cgroup rw,nosuid,nodev,noexec,relatime,memory 0 0
cgroup /sys/fs/cgroup/perf_event cgroup rw,nosuid,nodev,noexec,relatime,perf_event 0 0
cgroup /sys/fs/cgroup/freezer cgroup rw,nosuid,nodev,noexec,relatime,freezer 0 0
cgroup /sys/fs/cgroup/devices cgroup rw,nosuid,nodev,noexec,relatime,devices 0 0
cgroup /sys/fs/cgroup/pids cgroup rw,nosuid,nodev,noexec,relatime,pids 0 0
cgroup /sys/fs/cgroup/cpuset cgroup rw,nosuid,nodev,noexec,relatime,cpuset 0 0

</span><span class="gp">$</span><span class="w"> </span><span class="nb">cat</span> /proc/mounts | <span class="nb">grep </span>cgroup | <span class="nb">grep </span>devices
<span class="go">cgroup /sys/fs/cgroup/devices cgroup rw,nosuid,nodev,noexec,relatime,devices 0 0</span></code></pre></figure>

<p>So now we have the file path, let’s run that find command that the script is running.</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="gp">$</span><span class="w"> </span>find /sys/fs/cgroup/devices <span class="nt">-name</span> <span class="s2">"devenv"</span>
<span class="go">/sys/fs/cgroup/devices/lxc.payload/devenv
/sys/fs/cgroup/devices/lxc.monitor/devenv</span></code></pre></figure>

<p>It returns two entries which seem related and valid. So, what’s going on?</p>

<p>At this point I am thinking that <a href="https://linuxcontainers.org/">lxc</a> itself, the container implementation, may
be the problem. vagrant-lxc hasn’t been updated in a while.</p>

<p>Let’s find out (this is <a href="https://archlinux.org">archlinux</a> specific):</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="gp">$</span><span class="w"> </span><span class="nb">cat</span> /var/log/pacman.log | <span class="nb">grep </span>lxc | <span class="nb">grep </span>upgraded
<span class="gp">[2018-11-28 10:24] [ALPM] upgraded lxc (1:3.0.2-1 -&gt;</span><span class="w"> </span>1:3.0.3-1<span class="o">)</span>
<span class="gp">[2018-12-17 17:05] [ALPM] upgraded lxc (1:3.0.3-1 -&gt;</span><span class="w"> </span>1:3.1.0-1<span class="o">)</span></code></pre></figure>

<p>So it seems that lxc was upgraded a few weeks ago (it’s 2019-01 at the time of
this writing) and since I haven’t used it during these weeks I didn’t run into
this before.</p>

<p>My next step is to downgrade my installed version and see if that fixes this:</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="gp">$</span><span class="w"> </span><span class="nb">sudo </span>pacman <span class="nt">-U</span> lxc-1_3.0.3-1-x86_64.pkg.tar.xz
<span class="go">[sudo] password for fms:
loading packages...
</span><span class="gp">warning: downgrading package lxc (1:3.1.0-1 =&gt;</span><span class="w"> </span>1:3.0.3-1<span class="o">)</span>
<span class="go">
</span><span class="c">...
</span><span class="go">
</span><span class="gp">$</span><span class="w"> </span>vagrant up
<span class="go">Bringing machine 'default' up with 'lxc' provider...
</span><span class="gp">==&gt;</span><span class="w"> </span>default: Importing base box <span class="s1">'emptybox/ubuntu-bionic-amd64-lxc'</span>...
<span class="gp">==&gt;</span><span class="w"> </span>default: Checking <span class="k">if </span>box <span class="s1">'emptybox/ubuntu-bionic-amd64-lxc'</span> version <span class="s1">'0.1.1546433870'</span> is up to date...
<span class="gp">==&gt;</span><span class="w"> </span>default: Setting up mount entries <span class="k">for </span>shared folders...
<span class="gp">    default: /vagrant =&gt;</span><span class="w"> </span>/home/fms/Projects/local-devenv
<span class="gp">==&gt;</span><span class="w"> </span>default: Starting container...
<span class="gp">==&gt;</span><span class="w"> </span>default: Waiting <span class="k">for </span>machine to boot. This may take a few minutes...
<span class="go">    default: SSH address: 10.0.3.229:22
    default: SSH username: vagrant
    default: SSH auth method: private key
    default:
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default:
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
</span><span class="gp">==&gt;</span><span class="w"> </span>default: Machine booted and ready!</code></pre></figure>

<p>It works! So it was the newer version of lxc that broke my setup. Let’s see what
this version has in the cgroup mount point.</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="gp">$</span><span class="w"> </span>find /sys/fs/cgroup/devices <span class="nt">-name</span> <span class="s2">"devenv"</span>
<span class="go">/sys/fs/cgroup/devices/lxc/devenv</span></code></pre></figure>

<p>Yep. This returns one entry. The newer version changed that and it broke what
pipework expected. But now we know exactly what and we can fix it:</p>

<h2 id="the-fix">The fix</h2>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c"># Try to find a cgroup matching exactly the provided name.</span>
<span class="nv">M</span><span class="o">=</span><span class="si">$(</span>find <span class="s2">"</span><span class="nv">$CGROUPMNT</span><span class="s2">"</span> <span class="nt">-name</span> <span class="s2">"</span><span class="nv">$GUESTNAME</span><span class="s2">"</span><span class="si">)</span>
<span class="nv">N</span><span class="o">=</span><span class="si">$(</span><span class="nb">echo</span> <span class="s2">"</span><span class="nv">$M</span><span class="s2">"</span> | <span class="nb">wc</span> <span class="nt">-l</span><span class="si">)</span>
<span class="k">case</span> <span class="s2">"</span><span class="nv">$N</span><span class="s2">"</span> <span class="k">in
  </span>0<span class="p">)</span>
    <span class="c"># If we didn't find anything, try to lookup the container with Docker.</span>
    <span class="k">if </span>installed docker<span class="p">;</span> <span class="k">then
      </span><span class="nv">RETRIES</span><span class="o">=</span>3
      <span class="k">while</span> <span class="o">[</span> <span class="s2">"</span><span class="nv">$RETRIES</span><span class="s2">"</span> <span class="nt">-gt</span> 0 <span class="o">]</span><span class="p">;</span> <span class="k">do
        </span><span class="nv">DOCKERPID</span><span class="o">=</span><span class="si">$(</span>docker inspect <span class="nt">--format</span><span class="o">=</span><span class="s1">'{{ .State.Pid }}'</span> <span class="s2">"</span><span class="nv">$GUESTNAME</span><span class="s2">"</span><span class="si">)</span>
        <span class="nv">DOCKERCID</span><span class="o">=</span><span class="si">$(</span>docker inspect <span class="nt">--format</span><span class="o">=</span><span class="s1">'{{ .ID }}'</span> <span class="s2">"</span><span class="nv">$GUESTNAME</span><span class="s2">"</span><span class="si">)</span>
        <span class="nv">DOCKERCNAME</span><span class="o">=</span><span class="si">$(</span>docker inspect <span class="nt">--format</span><span class="o">=</span><span class="s1">'{{ .Name }}'</span> <span class="s2">"</span><span class="nv">$GUESTNAME</span><span class="s2">"</span><span class="si">)</span>

        <span class="o">[</span> <span class="s2">"</span><span class="nv">$DOCKERPID</span><span class="s2">"</span> <span class="o">!=</span> 0 <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="nb">break
        sleep </span>1
        <span class="nv">RETRIES</span><span class="o">=</span><span class="k">$((</span>RETRIES <span class="o">-</span> <span class="m">1</span><span class="k">))</span>
      <span class="k">done</span>

      <span class="o">[</span> <span class="s2">"</span><span class="nv">$DOCKERPID</span><span class="s2">"</span> <span class="o">=</span> 0 <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="o">{</span>
        die 1 <span class="s2">"Docker inspect returned invalid PID 0"</span>
      <span class="o">}</span>

      <span class="o">[</span> <span class="s2">"</span><span class="nv">$DOCKERPID</span><span class="s2">"</span> <span class="o">=</span> <span class="s2">"&lt;no value&gt;"</span> <span class="o">]</span> <span class="o">&amp;&amp;</span> <span class="o">{</span>
        die 1 <span class="s2">"Container </span><span class="nv">$GUESTNAME</span><span class="s2"> not found, and unknown to Docker."</span>
      <span class="o">}</span>
    <span class="k">else
      </span>die 1 <span class="s2">"Container </span><span class="nv">$GUESTNAME</span><span class="s2"> not found, and Docker not installed."</span>
    <span class="k">fi</span>
    <span class="p">;;</span>
  1<span class="p">)</span> <span class="nb">true</span> <span class="p">;;</span>
  2<span class="p">)</span>  <span class="c"># LXC &gt;=3.1.0 returns two entries from the cgroups mount instead of one.</span>
      <span class="nb">echo</span> <span class="s2">"</span><span class="nv">$M</span><span class="s2">"</span> | <span class="nb">grep</span> <span class="nt">-q</span> <span class="s2">"lxc</span><span class="se">\.</span><span class="s2">monitor"</span> <span class="o">||</span> die 1 <span class="s2">"Found more than one container matching </span><span class="nv">$GUESTNAME</span><span class="s2">."</span>
      <span class="p">;;</span>
  <span class="k">*</span><span class="p">)</span> die 1 <span class="s2">"Found more than one container matching </span><span class="nv">$GUESTNAME</span><span class="s2">."</span> <span class="p">;;</span>
<span class="k">esac</span></code></pre></figure>

<p>We add a new case, if we get exactly two entries from find, check if one of them
is lxc.monitor, one of lxc 3.1.0 new additions, and if so, don’t error out.</p>

<p>We bring lxc up to date, apply this patch by hand to vagrant-lxc pipework copy
and try again:</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="gp">$</span><span class="w"> </span>vagrant up
<span class="go">Bringing machine 'default' up with 'lxc' provider...
</span><span class="gp">==&gt;</span><span class="w"> </span>default: Importing base box <span class="s1">'emptybox/ubuntu-bionic-amd64-lxc'</span>...
<span class="gp">==&gt;</span><span class="w"> </span>default: Checking <span class="k">if </span>box <span class="s1">'emptybox/ubuntu-bionic-amd64-lxc'</span> version <span class="s1">'0.1.1546433870'</span> is up to date...
<span class="gp">==&gt;</span><span class="w"> </span>default: Setting up mount entries <span class="k">for </span>shared folders...
<span class="gp">    default: /vagrant =&gt;</span><span class="w"> </span>/home/fms/Projects/local-devenv
<span class="gp">==&gt;</span><span class="w"> </span>default: Starting container...
<span class="gp">==&gt;</span><span class="w"> </span>default: Waiting <span class="k">for </span>machine to boot. This may take a few minutes...
<span class="go">    default: SSH address: 10.0.3.100:22
    default: SSH username: vagrant
    default: SSH auth method: private key
    default:
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default:
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
</span><span class="gp">==&gt;</span><span class="w"> </span>default: Machine booted and ready!</code></pre></figure>

<p>Success!!</p>

<h2 id="conclusion">Conclusion</h2>

<p>We fixed our broken dev environment and everything is almost right in the world again.</p>

<p>The other thing we need to do, of course, is to submit <a href="https://github.com/jpetazzo/pipework/pull/231">this patch</a> to pipework
and submit another patch to vagrant-lxc to use this new version of pipework so
people that run it with lxc &gt;3.1.0 have a working setup again.</p>]]></content><author><name>Francisco Soto</name></author><summary type="html"><![CDATA[I personally consider “debugging” (figuring out how something works and how is it broken) a very important skill as a software developer and one that I don’t think it is taught explicitly. It is simply left for us to figure it out.]]></summary></entry><entry><title type="html">Improving Linux battery life and thermal efficiency</title><link href="https://ebobby.org/2018/09/01/improving-linux-battery-life/" rel="alternate" type="text/html" title="Improving Linux battery life and thermal efficiency" /><published>2018-09-01T00:00:00-07:00</published><updated>2023-06-05T22:58:27-07:00</updated><id>https://ebobby.org/2018/09/01/improving-linux-battery-life</id><content type="html" xml:base="https://ebobby.org/2018/09/01/improving-linux-battery-life/"><![CDATA[<p>I recently switched from MacOS to Linux for my personal/work machine. As much as
I like my new laptop I really missed all the battery life that my previous
Macbook Pro was capable of.</p>

<p>I decided to do something about it and improve battery life of my setup as much
as I could. This post is part me saving it for future reference and part sharing
it with you, the curious reader.</p>

<h2 id="setup">Setup</h2>

<p>I have a <a href="https://system76.com/laptops/oryx">System76’s Oryx Pro 4 (2018)</a> with the following characteristics:</p>

<ul>
  <li>CPU: Intel i7-8750H, 2.2 up to 4.1 GHz, 6 cores</li>
  <li>Display: 15.6” 1920x1080 144hz</li>
  <li>Discrete GPU: NVIDIA GeForce GTX 1060</li>
  <li>Memory: 32 GB dual-channel DDR4 @ 2400 MHz</li>
  <li>Storage: 500G Samsung NVME 970 EVO, 1TB Samsung SSD 860 EVO</li>
  <li>Communications: Gigabit Ethernet, Intel Wireless-AC WiFi, Bluetooth</li>
  <li>Camera: 1080p HD Webcam</li>
  <li>Battery: Embedded 4 Cells Polymer battery pack – 55Wh</li>
</ul>

<p>And I run the following software (at the time of this writing):</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="go">-------------
OS: Arch Linux x86_64
Host: Oryx Pro oryp4
Kernel: 4.18.5-arch1-1-ARCH
Packages: 900 (pacman)
Shell: zsh 5.5.1
WM: i3
------------------</span></code></pre></figure>

<h2 id="pop-os">Pop OS!</h2>

<p><a href="https://system76.com/">System76</a> has their own Linux distribution <a href="https://pop.system76.com/">Pop! OS</a>. By
default uses Gnome and comes preloaded with some good stuff and uses an older
version of the kernel. It must be the LTS kernel since they have to support it
and it would be crazy to support every version out there.</p>

<p>When I tested this machine with Pop OS! I got a discharge rate of about ~11-10W
idle, just powertop running. I do not remember if I had the keyboard backlight
on and what the screen backlight level was so it may not be a fair comparison
but it’s a good baseline.</p>

<h2 id="archlinux">Archlinux</h2>

<p>I use <a href="https://archlinux.org/">Archlinux</a> and i3-gaps which are way lighter than Ubuntu (Pop! OS base) and
Gnome. With my current setup I get ~8.5 W discharge rate while idling, just
running powertop.</p>

<p>This is before I run anything related to power savings so it comes to show how
much Linux has improved.</p>

<h2 id="power-savings-configuration">Power savings configuration</h2>

<h3 id="kernel-command-line">Kernel command line</h3>

<p>I boot the kernel adding the following to the command line:</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="go">nowatchdog</span></code></pre></figure>

<p>The interesting bit for our current discussion is the nowatchdog paramater which
<a href="https://01.org/linuxgraphics/gfx-docs/drm/admin-guide/kernel-parameters.html">turns off</a> lockup detectors in the kernel which saves a tiny fraction of power
and performance.</p>

<h3 id="kernel-module-configuration">Kernel module configuration</h3>

<p>I have the following kernel module configuration file:</p>

<figure class="highlight"><pre><code class="language-conf" data-lang="conf"><span class="c"># /etc/modprobe.d/power-management.conf
# Wifi power saving
</span><span class="n">options</span> <span class="n">iwlwifi</span> <span class="n">power_save</span>=<span class="m">1</span> <span class="n">d0i3_disable</span>=<span class="m">0</span> <span class="n">uapsd_disable</span>=<span class="m">0</span>
<span class="n">options</span> <span class="n">iwldvm</span> <span class="n">force_cam</span>=<span class="m">0</span>

<span class="c"># USB autosuspend
</span><span class="n">options</span> <span class="n">usbcore</span> <span class="n">autosuspend</span>=<span class="m">5</span>

<span class="c"># Sound card power save
</span><span class="n">options</span> <span class="n">snd_hda_intel</span> <span class="n">power_save</span>=<span class="m">1</span>

<span class="c"># Intel GPU
</span><span class="n">options</span> <span class="n">i915</span> <span class="n">enable_dc</span>=<span class="m">2</span> <span class="n">enable_fbc</span>=<span class="m">1</span></code></pre></figure>

<p>These are here for reference only and to show the concept of doing some research
on what drivers your machine is using and what do they need to have increased
power savings. You don’t want to copy these as-is because it may turn your
machine unusable.</p>

<h3 id="tlp--linux-advanced-power-management">TLP – Linux Advanced Power Management</h3>

<p>From their project page:</p>

<blockquote>
  <p>TLP brings you the benefits of advanced power management for Linux without the
need to understand every technical detail. TLP comes with a default
configuration already optimized for battery life, so you may just install and
forget it. Nevertheless TLP is highly customizable to fulfil your specific
requirements.</p>
</blockquote>

<p><a href="https://linrunner.de/en/tlp/tlp.html">TLP</a> is a daemon that turns on power saving flags in hardware, configures the
CPU, configures kernel parameters, etc, that are good for power savings. As the
project says, the defaults are pretty good but you can always tweak it a little
bit more.</p>

<p>My own configuration:</p>

<figure class="highlight"><pre><code class="language-conf" data-lang="conf"><span class="n">TLP_ENABLE</span>=<span class="m">1</span>
<span class="n">TLP_DEFAULT_MODE</span>=<span class="n">AC</span>
<span class="n">TLP_PERSISTENT_DEFAULT</span>=<span class="m">0</span>
<span class="n">DISK_IDLE_SECS_ON_AC</span>=<span class="m">0</span>
<span class="n">DISK_IDLE_SECS_ON_BAT</span>=<span class="m">5</span>
<span class="n">MAX_LOST_WORK_SECS_ON_AC</span>=<span class="m">15</span>
<span class="n">MAX_LOST_WORK_SECS_ON_BAT</span>=<span class="m">60</span>
<span class="n">CPU_SCALING_GOVERNOR_ON_AC</span>=<span class="n">powersave</span>
<span class="n">CPU_SCALING_GOVERNOR_ON_BAT</span>=<span class="n">powersave</span>
<span class="n">CPU_HWP_ON_AC</span>=<span class="n">balance_performance</span>
<span class="n">CPU_HWP_ON_BAT</span>=<span class="n">balance_power</span>
<span class="n">SCHED_POWERSAVE_ON_AC</span>=<span class="m">0</span>
<span class="n">SCHED_POWERSAVE_ON_BAT</span>=<span class="m">1</span>
<span class="n">NMI_WATCHDOG</span>=<span class="m">0</span>
<span class="n">ENERGY_PERF_POLICY_ON_AC</span>=<span class="n">performance</span>
<span class="n">ENERGY_PERF_POLICY_ON_BAT</span>=<span class="n">power</span>
<span class="n">DISK_IOSCHED</span>=<span class="s2">"keep"</span>
<span class="n">SATA_LINKPWR_ON_AC</span>=<span class="s2">"max_performance"</span>
<span class="n">SATA_LINKPWR_ON_BAT</span>=<span class="s2">"min_power"</span>
<span class="n">AHCI_RUNTIME_PM_TIMEOUT</span>=<span class="m">15</span>
<span class="n">PCIE_ASPM_ON_AC</span>=<span class="n">performance</span>
<span class="n">PCIE_ASPM_ON_BAT</span>=<span class="n">powersave</span>
<span class="n">RADEON_POWER_PROFILE_ON_AC</span>=<span class="n">high</span>
<span class="n">RADEON_POWER_PROFILE_ON_BAT</span>=<span class="n">low</span>
<span class="n">RADEON_DPM_STATE_ON_AC</span>=<span class="n">performance</span>
<span class="n">RADEON_DPM_STATE_ON_BAT</span>=<span class="n">battery</span>
<span class="n">RADEON_DPM_PERF_LEVEL_ON_AC</span>=<span class="n">auto</span>
<span class="n">RADEON_DPM_PERF_LEVEL_ON_BAT</span>=<span class="n">auto</span>
<span class="n">WIFI_PWR_ON_AC</span>=<span class="n">off</span>
<span class="n">WIFI_PWR_ON_BAT</span>=<span class="n">on</span>
<span class="n">WOL_DISABLE</span>=<span class="n">Y</span>
<span class="n">SOUND_POWER_SAVE_ON_AC</span>=<span class="m">0</span>
<span class="n">SOUND_POWER_SAVE_ON_BAT</span>=<span class="m">1</span>
<span class="n">SOUND_POWER_SAVE_CONTROLLER</span>=<span class="n">Y</span>
<span class="n">BAY_POWEROFF_ON_AC</span>=<span class="m">0</span>
<span class="n">BAY_POWEROFF_ON_BAT</span>=<span class="m">0</span>
<span class="n">BAY_DEVICE</span>=<span class="s2">"sr0"</span>
<span class="n">RUNTIME_PM_ON_AC</span>=<span class="n">on</span>
<span class="n">RUNTIME_PM_ON_BAT</span>=<span class="n">auto</span>
<span class="n">USB_AUTOSUSPEND</span>=<span class="m">1</span>
<span class="n">USB_BLACKLIST_BTUSB</span>=<span class="m">0</span>
<span class="n">USB_BLACKLIST_PHONE</span>=<span class="m">0</span>
<span class="n">USB_BLACKLIST_PRINTER</span>=<span class="m">1</span>
<span class="n">USB_BLACKLIST_WWAN</span>=<span class="m">1</span>
<span class="n">RESTORE_DEVICE_STATE_ON_STARTUP</span>=<span class="m">0</span></code></pre></figure>

<p>There is some fine tweaking possible for newer CPUs with TLP to manage cpu power
hints, pstates, turbo settings, etc. I don’t use them since I run System76’s
system76-power(github) to handle the CPU profiles because it is easier.</p>

<h4 id="archlinux-1">Archlinux</h4>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="go">sudo pacman -S tlp
sudo systemctl enable --now tlp.service
sudo tlp-stat</span></code></pre></figure>

<h3 id="fancontrol">fancontrol</h3>

<p><code class="language-plaintext highlighter-rouge">fancontrol</code> is a daemon, that as its name says, controls fans in your
computer. That is, if the computer maker decided to give you that power. In
Linux, such control is done through the PWM (Pulse Width Modulator) interface.</p>

<p>PWM allows you to control the power that goes into an electrical line (I am
being intentionally simplistic here). If you have fan PWM lines available you
can control how much power goes into them, and in turn, how fast they spin.</p>

<p><code class="language-plaintext highlighter-rouge">fancontrol</code> is part of the <code class="language-plaintext highlighter-rouge">lm_sensors</code> package in archlinux and ubuntu.</p>

<p><code class="language-plaintext highlighter-rouge">fancontrol</code> is a tool to control a fan speed based on some temperature
thresholds. This is useful because usually the auto control in the fans is
overly aggressive, turning fans on very rapidly when the CPU activity increases
a tiny bit. And, the faster a fan spins, the more power it consumes.</p>

<p>You have to take several things into account here, the temperature limits of the
hardware you want to control the fan of, the ambient temperature of where you
live. Say, if the CPU wants to be at 30C by default but your ambient temperature
is 40C those fans are going to spin forever. You want to be careful with
this. You most likely don’t want to fry your electronics.</p>

<p>If you install the <code class="language-plaintext highlighter-rouge">lm_sensors</code> package and run the sensors utility you can see
which hardware temperature monitors you have available and what are their
temperature limits. Plus, if you have fans around.</p>

<p>Mine looks like this:</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="gp">$</span><span class="w"> </span>sensors
<span class="go">coretemp-isa-0000
Adapter: ISA adapter
Package id 0:  +39.0°C  (high = +100.0°C, crit = +100.0°C)
Core 0:        +37.0°C  (high = +100.0°C, crit = +100.0°C)
Core 1:        +38.0°C  (high = +100.0°C, crit = +100.0°C)
Core 2:        +37.0°C  (high = +100.0°C, crit = +100.0°C)
Core 3:        +38.0°C  (high = +100.0°C, crit = +100.0°C)
Core 4:        +36.0°C  (high = +100.0°C, crit = +100.0°C)
Core 5:        +37.0°C  (high = +100.0°C, crit = +100.0°C)

system76-isa-0000
Adapter: ISA adapter
CPU fan:         2533 RPM
GPU fan:         4083 RPM
CPU temperature:  +38.0°C
GPU temperature:   +0.0°C</span></code></pre></figure>

<p>Now, that is one I care about, the system76-isa-0000 has a fan for the CPU, and
turns out it is very aggressive and since I live in a very warm place to begin
with, I want to tone it down so I can save a bit of juice.</p>

<p>Run the <code class="language-plaintext highlighter-rouge">pwmconfig</code> utility which will create an <code class="language-plaintext highlighter-rouge">/etc/fancontrol</code> file which
looks like this:</p>

<figure class="highlight"><pre><code class="language-conf" data-lang="conf"><span class="c"># Configuration file generated by pwmconfig, changes will be lost
</span><span class="n">INTERVAL</span>=<span class="m">10</span>
<span class="n">DEVPATH</span>=<span class="n">hwmon1</span>=<span class="n">devices</span>/<span class="n">platform</span>/<span class="n">system76</span>
<span class="n">DEVNAME</span>=<span class="n">hwmon1</span>=<span class="n">system76</span>
<span class="n">FCTEMPS</span>= <span class="n">hwmon1</span>/<span class="n">pwm1</span>=<span class="n">hwmon1</span>/<span class="n">temp1_input</span>
<span class="n">FCFANS</span>= <span class="n">hwmon1</span>/<span class="n">pwm1</span>=<span class="n">hwmon1</span>/<span class="n">fan1_input</span>
<span class="n">MINTEMP</span>= <span class="n">hwmon1</span>/<span class="n">pwm1</span>=<span class="m">40</span>
<span class="n">MAXTEMP</span>= <span class="n">hwmon1</span>/<span class="n">pwm1</span>=<span class="m">75</span>
<span class="n">MINSTART</span>= <span class="n">hwmon1</span>/<span class="n">pwm1</span>=<span class="m">14</span>
<span class="n">MINSTOP</span>= <span class="n">hwmon1</span>/<span class="n">pwm1</span>=<span class="m">12</span></code></pre></figure>

<p>(Don’t copy this. This is very hardware dependant, run pwmconfig)</p>

<p>Enable the <code class="language-plaintext highlighter-rouge">fancontrol.service</code> and voila.</p>

<h4 id="archlinux-2">Archlinux</h4>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="go">sudo pacman -S lm_sensors
sudo pwmconfig
sudo systemctl enable --now fancontrol.service</span></code></pre></figure>

<p>Fans are now under control.</p>

<h3 id="thermald">thermald</h3>

<p><a href="https://01.org/linux-thermal-daemon/documentation/introduction-thermal-daemon">Linux thermal daemon</a> (thermald) monitors and controls temperature in
laptops, tablets PC with the latest Intel sandy bridge and latest Intel CPU
releases. Once the system temperature reaches a certain threshold, the Linux
daemon activates various cooling methods to try to cool the system.</p>

<p>Now, the difference between the fans and thermald is that thermald uses passive
cooling methods built into these CPUs.</p>

<p><code class="language-plaintext highlighter-rouge">thermald</code> is a last barrier. When the CPU is under heavy load and not even the
fan can keep it cool thermald uses several passive cooling features built into
newer cpus (Intel’s, not sure about AMD) to minimize power and temperature.</p>

<p>This is my current configuration:</p>

<figure class="highlight"><pre><code class="language-xml" data-lang="xml"><span class="c">&lt;!-- /etc/thermald/thermal-conf.xml --&gt;</span>
<span class="cp">&lt;?xml version="1.0"?&gt;</span>
<span class="nt">&lt;ThermalConfiguration&gt;</span>
  <span class="nt">&lt;Platform&gt;</span>
    <span class="nt">&lt;Name&gt;</span>Override CPU Passive<span class="nt">&lt;/Name&gt;</span>
    <span class="nt">&lt;ProductName&gt;</span>*<span class="nt">&lt;/ProductName&gt;</span>
    <span class="nt">&lt;Preference&gt;</span>QUIET<span class="nt">&lt;/Preference&gt;</span>
    <span class="nt">&lt;ThermalZones&gt;</span>
      <span class="nt">&lt;ThermalZone&gt;</span>
        <span class="nt">&lt;Type&gt;</span>cpu<span class="nt">&lt;/Type&gt;</span>
        <span class="nt">&lt;TripPoints&gt;</span>
          <span class="nt">&lt;TripPoint&gt;</span>
            <span class="nt">&lt;Temperature&gt;</span>82000<span class="nt">&lt;/Temperature&gt;</span>
            <span class="nt">&lt;type&gt;</span>passive<span class="nt">&lt;/type&gt;</span>
            <span class="nt">&lt;ControlType&gt;</span>SEQUENTIAL<span class="nt">&lt;/ControlType&gt;</span>
          <span class="nt">&lt;/TripPoint&gt;</span>
        <span class="nt">&lt;/TripPoints&gt;</span>
      <span class="nt">&lt;/ThermalZone&gt;</span>
    <span class="nt">&lt;/ThermalZones&gt;</span>
  <span class="nt">&lt;/Platform&gt;</span>
<span class="nt">&lt;/ThermalConfiguration&gt;</span></code></pre></figure>

<p>Which pretty much means that if the CPU goes higher than 82C it should use all
matters of passive cooling at its disposal to cool it down. I’ve tested this and
it’s amazing how even using all the cores heavily it stays at 82C.</p>

<p>After you configure it you just enable the daemon and that’s it. It will do it’s
thing when it must.</p>

<h4 id="archlinux-3">Archlinux</h4>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="go">sudo pacman -S thermald
sudo systemctl enable --now thermald.service</span></code></pre></figure>

<h3 id="undervolting">Undervolting</h3>

<p>Note: This is experimental.</p>

<p>Undervolting is similar to overclocking, except instead of increasing speed, you
are trying to keep your current speed by using less voltage.</p>

<p>This works because due to tiny differences in manufacturing processes CPU
voltage and power specifications are not the same between units. They have
ranges and tolerances. This is what lets you overclock and undervolt.</p>

<p>When a CPU maker builds a CPU they make sure to feed it voltage which can
guarantee they meet the specified clock speed. But it is very likely any given
CPU can work with less voltage and still work within the specified speeds and
stability.</p>

<p>What this accomplishes for us is using less power to feed the CPU and that saves
juice. Less voltage also mean less heat, which in turn means less fan noise and
spinning, which also saves juice.</p>

<p>I use <a href="https://github.com/mihic/linux-intel-undervolt">intel-undervolt</a> to set the voltages, but there are
other scripts and tools available.</p>

<p>You have to find the specific voltages and tolerances that your CPU can work
with, you do this by decreasing the voltage just a tiny bit, do some heavy
stress testing for a good chunk of time. If it works, you decrease it a bit
more, and you continue doing it till you find a set of voltages that work for
your machine.</p>

<p>This is how my (current) <code class="language-plaintext highlighter-rouge">/etc/intel-undervolt.conf</code> looks like:</p>

<figure class="highlight"><pre><code class="language-conf" data-lang="conf"><span class="c"># CPU Undervolting
# Usage: apply ${index} ${display_name} ${undervolt_value}
# Example: apply 2 'CPU Cache' -25.84
</span>
<span class="n">apply</span> <span class="m">0</span> <span class="s1">'CPU'</span> -<span class="m">170</span>
<span class="c">#apply 1 'GPU' 0
</span><span class="n">apply</span> <span class="m">2</span> <span class="s1">'CPU Cache'</span> -<span class="m">170</span>
<span class="c">#apply 3 'System Agent' 0
#apply 4 'Analog I/O' 0
</span>
<span class="c"># TDP Alteration
# Usage: tdp ${short_term} ${long_term}
# Usage: tdp ${short_term}/${time_window} ${long_term}/${time_window}
# Example: tdp 45/0.002 35/28
</span>
<span class="c"># Critical Temperature Offset Alteration
# Usage: tjoffset ${temperature_offset}
# Example: tjoffset -20
</span>
<span class="c"># Daemon Update Inverval
# Usage: interval ${interval_in_milliseconds}
</span>
<span class="n">interval</span> <span class="m">5000</span></code></pre></figure>

<p>The reason the GPU is commented out is because apparently it is already
undervolted by quite a bit by default. I recommend you use <code class="language-plaintext highlighter-rouge">sudo intel-undervolt
read</code> to check your default values. You may find something you don’t want to
change.</p>

<h4 id="archlinux-4">Archlinux</h4>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="gp">git clone https://aur.archlinux.org/intel-undervolt.git ;</span><span class="w"> </span><span class="nb">cd </span>intel-undervolt
<span class="go">makepkg -sci
sudo systemctl enable --now intel-undervolt.service</span></code></pre></figure>

<h2 id="results">Results</h2>

<p>With all of this configuration, my idle power usage doesn’t really change very
much. It starts showing when you actually put some load and start using the
system. My overall battery life went from about ~4 hours to about ~5.5
hours. Which is pretty good. It still sucks compared with the MBP but this
machine is a power house. I didn’t really expect to get 10 hours of battery with
it.</p>

<p>Thermals improved quite a bit too, and that’s pretty important to me because I
live in a very hot place.</p>

<p>That is it. If you have any suggestions how to improve this guide, or how to
increase battery life please do share!</p>]]></content><author><name>Francisco Soto</name></author><summary type="html"><![CDATA[I recently switched from MacOS to Linux for my personal/work machine. As much as I like my new laptop I really missed all the battery life that my previous Macbook Pro was capable of.]]></summary></entry><entry><title type="html">HOWTO: Use Oryx Pro’s discrete GPU in Archlinux</title><link href="https://ebobby.org/2018/08/06/archlinux-nvidia-on-oryp4/" rel="alternate" type="text/html" title="HOWTO: Use Oryx Pro’s discrete GPU in Archlinux" /><published>2018-08-06T00:00:00-07:00</published><updated>2023-06-05T22:58:27-07:00</updated><id>https://ebobby.org/2018/08/06/archlinux-nvidia-on-oryp4</id><content type="html" xml:base="https://ebobby.org/2018/08/06/archlinux-nvidia-on-oryp4/"><![CDATA[<p><em>Although this has only been tested in <a href="https://system76.com/laptops/oryx">System76’s Oryx Pro</a> I suppose it
can be used in other distributions as well with a bit of work.</em></p>

<h2 id="introduction">Introduction</h2>

<p>In a <a href="https://ebobby.org/2018/07/15/archlinux-on-oryp4/">previous post</a> I explained how to use your discrete GPU for
specific applications while still running your main screen on your integrated
GPU.</p>

<p>This is pretty convenient because you don’t have to reboot to play a game or use
your GPU in a specific application, but it made other things more difficult like
using external monitors since they are all wired to the discrete GPU.</p>

<p>In this post we will replicate <a href="https://system76.com/pop">Pop! OS</a> ability to boot exclusively into
your discrete GPU and have the ability to use external monitors (technically you
can use external monitors with bumblebee but it requires <a href="https://wiki.archlinux.org/index.php/Bumblebee#Multiple_monitors">some effort</a>).</p>

<h2 id="no-bumblebee">No bumblebee</h2>

<p>If you have installed bumblebee we have to get rid of it as it includes some
configuration to blacklist nvidia drivers and that will interfere with our set
up.</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="go">sudo systemctl disable --now bumblebeed.service
sudo pacman -Rns bumblebee
sudo gpasswd -d fms bumblebee</span></code></pre></figure>

<h2 id="install-required-software">Install required software</h2>

<p>You will need this packages to pull this off:</p>

<h3 id="install-drivers">Install drivers</h3>

<p>There is usually a discussion about using nouveau and how <a href="https://www.nvidia.com/">Nvidia</a> is
evil and how much they suck but I still want to get as much of my video card as
I am able (with Linux) so I use their drivers.</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="go">sudo pacman -S nvidia nvidia-utils nvidia-settings</span></code></pre></figure>

<h3 id="install-system76-power">Install system76-power</h3>

<p>This package is available in the <a href="https://aur.archlinux.org/">AUR</a>. I personally use aurman.</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="go">aurman -S system76-power</span></code></pre></figure>

<p>This utility allows you to switch GPU’s, turn on the discrete card on or off, or
switch power profiles. Overall, if you have a System76 machine you want this.</p>

<h3 id="configuration">Configuration</h3>

<p>Something important of note is that (at the time of this writing) the
nvidia-utils package includes the following (very important) configuration file:</p>

<figure class="highlight"><pre><code class="language-conf" data-lang="conf"><span class="c"># /usr/share/X11/xorg.conf.d/10-nvidia-drm-outputclass.conf
</span><span class="n">Section</span> <span class="s2">"OutputClass"</span>
    <span class="n">Identifier</span> <span class="s2">"intel"</span>
    <span class="n">MatchDriver</span> <span class="s2">"i915"</span>
    <span class="n">Driver</span> <span class="s2">"modesetting"</span>
<span class="n">EndSection</span>

<span class="n">Section</span> <span class="s2">"OutputClass"</span>
    <span class="n">Identifier</span> <span class="s2">"nvidia"</span>
    <span class="n">MatchDriver</span> <span class="s2">"nvidia-drm"</span>
    <span class="n">Driver</span> <span class="s2">"nvidia"</span>
    <span class="n">Option</span> <span class="s2">"AllowEmptyInitialConfiguration"</span>
    <span class="n">Option</span> <span class="s2">"PrimaryGPU"</span> <span class="s2">"yes"</span>
    <span class="n">ModulePath</span> <span class="s2">"/usr/lib/nvidia/xorg"</span>
    <span class="n">ModulePath</span> <span class="s2">"/usr/lib/xorg/modules"</span>
<span class="n">EndSection</span></code></pre></figure>

<p>It enables the modsetting driver and loads the nvidia driver (if
available). This fortunately doesn’t get in the way when using the Intel GPU.</p>

<p>One thing to note is that using the modesetting driver breaks xbacklight. If you
don’t use a desktop environment and rely on it to change the screen backlight
you will have to change how you do it.</p>

<h3 id="telling-xorg-to-use-the-discrete-gpu">Telling Xorg to use the discrete GPU</h3>

<p>In order to use your discrete GPU we depend on a technology called <a href="https://forums.developer.nvidia.com/t/prime-and-prime-synchronization/44423">PRIME</a>. It
allows you to offload rendering to different GPUs, and more importantly for the
matter at hand, to have one GPU (integrated) to display the output rendered by
another GPU (your discrete).</p>

<p>Where you put the following instructions depends on how you run your X server.
Run startx manually</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c"># Check if we are using nvidia graphics and tell xorg to get the</span>
<span class="c"># output from NVIDIA-0.</span>
<span class="k">if </span>system76-power graphics | <span class="nb">grep</span> <span class="nt">-q</span> <span class="s1">'nvidia'</span><span class="p">;</span> <span class="k">then
  </span>xrandr <span class="nt">--setprovideroutputsource</span> modesetting NVIDIA-0
  xrandr <span class="nt">--auto</span>
  xrandr <span class="nt">--dpi</span> 96
<span class="k">fi</span></code></pre></figure>

<p>You can put them in <code class="language-plaintext highlighter-rouge">.xinitrc.</code></p>

<h3 id="using-a-desktop-manager">Using a desktop manager</h3>

<p>Create the following script:</p>

<figure class="highlight"><pre><code class="language-shell" data-lang="shell"><span class="c">#!/bin/sh</span>
<span class="c">#/usr/bin/setup-nvidia.sh</span>

<span class="k">if </span>system76-power graphics | <span class="nb">grep</span> <span class="nt">-q</span> <span class="s1">'nvidia'</span><span class="p">;</span> <span class="k">then
  </span>xrandr <span class="nt">--setprovideroutputsource</span> modesetting NVIDIA-0
  xrandr <span class="nt">--auto</span>
  xrandr <span class="nt">--dpi</span> 96
<span class="k">fi</span></code></pre></figure>

<p>And use one of the following methods mentioned <a href="https://wiki.archlinux.org/index.php/NVIDIA_Optimus#Display_Managers">here</a> as it is
dependant of which manager you use.</p>

<h2 id="conclusion">Conclusion</h2>

<p>That is it!. Now you just need to run:</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="go">sudo system76-power graphics nvidia</span></code></pre></figure>

<p>Reboot.</p>

<p>Enjoy. If you can’t get it working or have any suggestion, please reach out.</p>]]></content><author><name>Francisco Soto</name></author><summary type="html"><![CDATA[Although this has only been tested in System76’s Oryx Pro I suppose it can be used in other distributions as well with a bit of work.]]></summary></entry><entry><title type="html">Installing Archlinux on System76’s 2018 Oryx Pro (oryp4)</title><link href="https://ebobby.org/2018/07/15/archlinux-on-oryp4/" rel="alternate" type="text/html" title="Installing Archlinux on System76’s 2018 Oryx Pro (oryp4)" /><published>2018-07-15T00:00:00-07:00</published><updated>2023-06-05T22:58:27-07:00</updated><id>https://ebobby.org/2018/07/15/archlinux-on-oryp4</id><content type="html" xml:base="https://ebobby.org/2018/07/15/archlinux-on-oryp4/"><![CDATA[<p>I recently decided to move from OSX to Linux and settled on a System76 new Oryx
Pro (Jun 2018). They officially support Ubuntu and their own <a href="https://system76.com/pop">Pop! OS</a>
which work really well. I decided to use Archlinux though as it is my
distribution of choice.</p>

<p>This post contains the details of how I managed to set it up and get all the
functionality you get in Pop! OS.</p>

<h2 id="turn-off-discrete-graphics-card">Turn off discrete graphics card</h2>

<p>This machine has a Nvidia discrete graphics card, either a 1060/1070 GTX
depending on your configuration. Because of the way this card interacts with the
kernel ACPI code there are times when it can hang up the system if you run <code class="language-plaintext highlighter-rouge">lspci</code>
or some sort of hardware scanning tool like <code class="language-plaintext highlighter-rouge">neofetch</code>.</p>

<p>Let’s turn it off during install to avoid running into any problems :</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="gp">echo 1 &gt;</span><span class="w"> </span>/sys/bus/pci/devices/0000:01:00.0/remove</code></pre></figure>

<p>Don’t worry. We’ll get it working later on.</p>

<h2 id="initial-install">Initial install</h2>

<p>Do a regular <a href="https://wiki.archlinux.org/index.php/Installation_guide">UEFI install</a>. You have you make sure that your EFI
partition is <code class="language-plaintext highlighter-rouge">/boot/efi</code> instead of <code class="language-plaintext highlighter-rouge">/boot</code> since this is required for
System76’s firmware update utilities to work.</p>

<p>My setup looks like this:</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="go">Device            Start       End   Sectors   Size Type
/dev/nvme0n1p1     2048    411647    409600   200M EFI System
/dev/nvme0n1p2   411648  17188863  16777216     8G Linux swap
/dev/nvme0n1p3 17188864 976773134 959584271 457.6G Linux filesystem

Filesystem      Size  Used Avail Use% Mounted on
/dev/nvme0n1p1  197M  122K  197M   1% /boot/efi
/dev/nvme0n1p3  450G   19G  408G   5% /</span></code></pre></figure>

<h2 id="boot-loader">Boot loader</h2>

<p>System76’s utilities require you to pass <code class="language-plaintext highlighter-rouge">ec_sys.write_support=1</code> as an argument
for the kernel. You need to configure your bootloader for that. In my case I use
grub so here is my <code class="language-plaintext highlighter-rouge">/etc/defaults/grub</code> changes to pass this on and a few other
(like the resume swap partition for hibernation).</p>

<figure class="highlight"><pre><code class="language-conf" data-lang="conf">...
<span class="n">GRUB_TIMEOUT</span> = <span class="m">0</span>
<span class="n">GRUB_CMDLINE_LINUX_DEFAULT</span>=<span class="s2">"quiet nowatchdog ec_sys.write_support=1 resume=/dev/nvme0n1p2"</span>
...</code></pre></figure>

<p>Install it:</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="go">grub-install --target=x86_64-efi --efi-directory=/boot/efi
grub-mkconfig -o /boot/grub/grub.cfg</span></code></pre></figure>

<h1 id="system76-utilities-post-setup">System76 utilities (post-setup)</h1>

<p>System76 offers some utilities for Ubuntu and Pop! OS to enable better support
for their hardware and firmware upgrades. The <a href="https://aur.archlinux.org">AUR</a> has all of these packages
too. <em>Disclaimer: I maintain a couple of them.</em></p>

<h2 id="system76-dkms-aur">system76-dkms (aur)</h2>

<p>This is system76 kernel module that gives support to the keyboard backlight,
nvidia sound (hdmi) and a few other things. If you have a System76 machine you
definitely want this:</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="go">git clone https://aur.archlinux.org/system76-dkms.git
cd system76-dkms
makepkg -sci
sudo modprobe system76</span></code></pre></figure>

<h2 id="system76-firmware-daemon-aur">system76-firmware-daemon (aur)</h2>

<p>This package contains a daemon that is used by <code class="language-plaintext highlighter-rouge">system76-driver</code> to schedule
firmware updates.</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="go">git clone https://aur.archlinux.org/system76-firmware-daemon.git
cd system76-firmware-daemon
makepkg -sci
sudo systemctl enable --now system76-firmware-daemon.service</span></code></pre></figure>

<h2 id="system76-driver-aur">system76-driver (aur)</h2>

<p>This package contains the interface to the firmware daemon and a few other
utilities. You don’t really need most of them in Archlinux. They are mostly
support for older hardware.</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="go">git clone https://aur.archlinux.org/system76-driver.git
cd system76-driver
makepkg -sci
</span><span class="gp">sudo system76-firmware #</span><span class="w"> </span>So you can tell <span class="k">if </span>you have new firmware</code></pre></figure>

<p>This is how the firmware UI tool looks like:</p>

<p><img class="center" src="/assets/images/firmware.png" /></p>

<h2 id="system76-power-aur">system76-power (aur)</h2>

<p>This is the utility you want to set up some pre-configured power management
profiles for your machine and to control the Nvidia discrete graphics card.</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="go">git clone https://aur.archlinux.org/system76-power.git
cd system76-power
makepkg -sci
sudo systemctl enable --now system76-power.service</span></code></pre></figure>

<h1 id="managing-the-discrete-graphics-card">Managing the discrete graphics card</h1>

<p>System76 Pop! OS setup is designed to make you switch between Intel and Nvidia,
reboot and then use that card solely during that session. I don’t find that
particularly good since this is a laptop and I want to maximize battery life as
much as I can. I want to run Intel’s card and use the discrete card only on
demand.</p>

<p>In order to accomplish this, I use <a href="https://wiki.archlinux.org/index.php/bumblebee">bumblebee</a> and
<a href="https://wiki.archlinux.org/index.php/bumblebee#Primusrun">primus</a>. I don’t use <a href="https://wiki.archlinux.org/index.php/bumblebee#Default_power_state_of_NVIDIA_card_using_bbswitch">bbswitch</a> which is used to control the
discrete graphics card power since it is unstable in this machine (at this point
in time) and we can use system76-power to turn the card on and off more
reliably.</p>

<h2 id="setup">Setup</h2>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="go">sudo pacman -S bumblebee primus
</span><span class="gp">sudo gpasswd -a &lt;username&gt;</span><span class="w"> </span>bumblebee    <span class="c"># need to be part of this group</span>
<span class="go">sudo systemctl enable --now bumblebeed.service
</span><span class="gp">sudo pacman -S nvidia nvidia-settings   #</span><span class="w"> </span>You can use nouveau also.
<span class="go">sudo system76-power graphics intel</span></code></pre></figure>

<p>And reboot.</p>

<h2 id="turn-card-on">Turn card on</h2>

<p>If you want to use the discrete card:</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="go">sudo system76-power graphics power on
sudo modprobe nvidia-drm nvidia-modeset nvidia
primusrun glxinfo | grep -i renderer
OpenGL renderer string: GeForce GTX 1060/PCIe/SSE</span></code></pre></figure>

<h2 id="turn-card-off">Turn card off</h2>

<p>Run the following:</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="go">sudo system76-power graphics power off</span></code></pre></figure>

<p>You will probably get the following error message:</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="go">system76-power: "device in use"</span></code></pre></figure>

<p>You have to unload the graphics drivers first:</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="go">sudo rmmod nvidia-drm nvidia-modeset nvidia
sudo system76-power graphics power off</span></code></pre></figure>

<p>You can check that it worked with:</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="go">lspci | grep -i vga
00:02.0 VGA compatible controller: Intel Corporation Device 3e9b</span></code></pre></figure>

<h2 id="aliases">Aliases</h2>

<p>In order to make this easier I use the following aliases:</p>

<figure class="highlight"><pre><code class="language-console" data-lang="console"><span class="go">alias nvidia-settings="optirun -b none nvidia-settings -c :8 "
</span><span class="gp">alias nvidia-on="sudo system76-power graphics power on ;</span><span class="w"> </span><span class="nb">sudo </span>modprobe nvidia-drm nvidia-modeset nvidia<span class="s2">"
</span><span class="gp">alias nvidia-off="sudo rmmod nvidia-drm nvidia-modeset nvidia ;</span><span class="w"> </span><span class="s2">sudo system76-power graphics power off"</span></code></pre></figure>

<h1 id="not-testedworking">Not tested/working</h1>

<ul>
  <li>There seems to be a fingerprint reader in this machine but the default kernel
doesn’t seem to pick it up and I haven’t made much of an effort to try to make
it work. I didn’t test it on Pop! OS. If you know which driver it needs please
let me know.</li>
  <li>Multiple monitors, I don’t have an external monitor so I haven’t tested
this. Depending on which graphics card the HDMI/Display ports are wired the
setup would be different. I’ll update the post once I try this.</li>
</ul>

<p>The webcam, sd card reader, sound, wifi, ethernet work out of the box.</p>

<p>I really hope this guide works for you as it took me a while to figure all this
out (and even had to bring a bring a couple of System76’s utilities to our own
AUR).</p>]]></content><author><name>Francisco Soto</name></author><summary type="html"><![CDATA[I recently decided to move from OSX to Linux and settled on a System76 new Oryx Pro (Jun 2018). They officially support Ubuntu and their own Pop! OS which work really well. I decided to use Archlinux though as it is my distribution of choice.]]></summary></entry><entry><title type="html">Algunas citas de Jack Ma</title><link href="https://ebobby.org/2014/02/27/Algunas-citas-de-Jack-Ma/" rel="alternate" type="text/html" title="Algunas citas de Jack Ma" /><published>2014-02-27T00:00:00-07:00</published><updated>2024-03-06T20:43:52-07:00</updated><id>https://ebobby.org/2014/02/27/Algunas-citas-de-Jack-Ma</id><content type="html" xml:base="https://ebobby.org/2014/02/27/Algunas-citas-de-Jack-Ma/"><![CDATA[<p>Estaba leyendo una nota de <a href="http://en.wikipedia.org/wiki/Jack_Ma">Jack Ma</a>, un exitoso empresario chino, acerca de como ser exitoso en la vida y los negocios. Algunas citas me parecieron muy buenas asi que las comparto (y traduzco). La nota original esta <a href="http://sg.news.yahoo.com/billionaire-jack-ma-teaches-successful-020018631.html">aqui</a>.</p>

<blockquote>
  <p>No creas por un momento que podras unificar los pensamientos de todos. Es imposible. Es mucho mas sencillo unificar a una compañia bajo una meta comun que alrededor de una persona en particular.</p>
</blockquote>

<blockquote>
  <p>No importa que tan exitoso seas en tu carrera, siempre debes recordar que estamos aqui para vivir. Si te mantienes ocupado trabajando todo el tiempo, seguramente vas a lamentarlo.</p>
</blockquote>

<blockquote>
  <p>El mundo no recordara lo que dices, pero ciertamente recordara lo que haz hecho.</p>
</blockquote>

<blockquote>
  <p>Si tienes una mentalidad diferente, tendras un resultado diferente: Si tomas decisiones diferentes a las de la gente, tu vida sera entonces diferente a las de ellos. <small>Jack Ma</small></p>
</blockquote>]]></content><author><name>Francisco Soto</name></author><summary type="html"><![CDATA[Estaba leyendo una nota de Jack Ma, un exitoso empresario chino, acerca de como ser exitoso en la vida y los negocios. Algunas citas me parecieron muy buenas asi que las comparto (y traduzco). La nota original esta aqui.]]></summary></entry><entry><title type="html">Uno de esos días</title><link href="https://ebobby.org/2014/02/09/Uno-de-esos-dias/" rel="alternate" type="text/html" title="Uno de esos días" /><published>2014-02-09T00:00:00-07:00</published><updated>2024-03-06T20:43:52-07:00</updated><id>https://ebobby.org/2014/02/09/Uno-de-esos-dias</id><content type="html" xml:base="https://ebobby.org/2014/02/09/Uno-de-esos-dias/"><![CDATA[<p><img class="center" src="/assets/images/photos/not-so-good-day.jpg" alt="Después de un día no tan bueno" /></p>

<h2 id="después-de-un-largo-y-no-tan-buen-día">Después de un largo y no tan buen día.</h2>

<p>Hoy tuve un día como pocos.</p>

<ul>
  <li>Perdí un vuelo a <a href="http://en.wikipedia.org/wiki/Phoenix,_Arizona">Phoenix</a> donde tenia mi conexión a San Francisco.</li>
  <li>Se me cayo mi <a href="http://en.wikipedia.org/wiki/Nexus_4">Nexus 4</a> y se quebró la pantalla inutilizando el touch en 3/4 de ella.</li>
  <li>Llegue a Phoenix (en auto) 15 minutos mas tarde de lo que necesitaba para alcanzar el ultimo vuelo a San Francisco.</li>
  <li>Hice una reservación a un hotel a través de <a href="http://www.expedia.com">Expedia</a> y me equivoque de fecha (mañana), tratar de resolver el problema cuando mi único medio de comunicación (internet/teléfono) era un celular con la pantalla no funcional fue toda una odisea.</li>
</ul>

<p>Y heme aquí, en el hotel en un área “folclórica” de Phoenix, esperando que todo salga bien para tomar mi vuelo mañana a San Francisco.</p>

<p>En vez de aguitarme o encabronarme me da risa.</p>

<p>La verdad de las cosas es que me considero una persona afortunada y un día como hoy, que son muy pocos, solo me hacen reafirmarlo.</p>

<p>También tomemos en cuenta que mis problemas de “<em>hombre blanco</em>” no tienen ninguna relevancia en el gran esquema de las cosas y hay personas que enfrentan cosas mucho peores todos los días.</p>

<p>Dejemos de quejarnos raza, levantémonos y hagamos algo.</p>]]></content><author><name>Francisco Soto</name></author><summary type="html"><![CDATA[]]></summary></entry></feed>