Skip to main content

Blog


Looking for old posts? Visit the archive.

How to Compile Dynamic NGINX Modules

Compiling dynamic NGINX modules is something that's kind of hard to find online. Especially if you want to compile them for an existing compiled copy of NGINX. This guide is going to walk through compiling the Upload module for NGINX on Ubuntu 18.04 using nginx from their repositories.

NGINX has already been compiled for us with various different options. If you install the nginx-extras package you get NGINX compiled with a bunch of modules, but you can also use the nginx-full or just nginx packages to get less modules.

To compile our new dynamic module, we'll need to download the source code for NGINX, install any dependencies used when the Ubuntu package maintainers compiled it, and use the same flags they did.

Installing dependencies

You'll first need to install any dependencies for the NGINX modules you're compiling. For me, I needed the following:

sudo apt install libperl-dev libgeoip-dev libgd-dev

Downloading NGINX and module source code

You'll need to compile this against the same version of NGINX that's currently installed. You can find that by running nginx -v. For me, we're using 1.14.0 so we'll download the source for that version.

wget https://nginx.org/download/nginx-1.14.0.tar.gz
tar zxf nginx-1.14.0.tar.gz

Then we need to download the source for our module. We're using the Upload module which can be found on Github. We'll grab the latest zip.

wget https://github.com/fdintino/nginx-upload-module/archive/master.zip
unzip master.zip

Find the NGINX compile flags

To make our module compatible with the existing NGINX binary, we need to use the same compile flags. We can find those by running the following command:

nginx -V

# nginx version: nginx/1.14.0 (Ubuntu)
built with OpenSSL 1.1.0g  2 Nov 2017
TLS SNI support enabled
configure arguments: --with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-mcUg8N/nginx-1.14.0=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -fPIC' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_flv_module --with-http_geoip_module=dynamic --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_mp4_module --with-http_perl_module=dynamic --with-http_random_index_module --with-http_secure_link_module --with-http_sub_module --with-http_xslt_module=dynamic --with-mail=dynamic --with-mail_ssl_module --with-stream=dynamic --with-stream_ssl_module --with-stream_ssl_preread_module --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/http-headers-more-filter --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/http-auth-pam --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/http-cache-purge --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/http-dav-ext --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/http-ndk --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/http-echo --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/http-fancyindex --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/nchan --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/http-lua --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/rtmp --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/http-uploadprogress --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/http-upstream-fair --add-dynamic-module=/build/nginx-mcUg8N/nginx-1.14.0/debian/modules/http-subs-filter

Now this is a LOT of flags, but we can ignore any of the ones for --add-dynamic-module because we don't need to compile those modules, just our new one. That leaves us with the following flags:

--with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-mcUg8N/nginx-1.14.0=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -fPIC' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_flv_module --with-http_geoip_module=dynamic --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_mp4_module --with-http_perl_module=dynamic --with-http_random_index_module --with-http_secure_link_module --with-http_sub_module --with-http_xslt_module=dynamic --with-mail=dynamic --with-mail_ssl_module --with-stream=dynamic --with-stream_ssl_module --with-stream_ssl_preread_module

WARNING If you don't do this, you'll end up with an error later on when you try to load the module that looks like this: # module "/usr/lib/nginx/modules/ngx_http_upload_module.so" is not binary compatible in /etc/nginx/modules-enabled/50-mod-http-upload.conf:1

Make sure you use these same flags when compiling so you don't get this error.

Compiling the module

Now we just need to hop into the nginx source directory and compile the module. We will add the flag to compile our new dynamic module at the end after the other flags --add-dynamic-module=../nginx-upload-module-master

cd nginx-1.14.0

./configure --with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-mcUg8N/nginx-1.14.0=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -Wl,-z,relro -Wl,-z,now -fPIC' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_flv_module --with-http_geoip_module=dynamic --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_mp4_module --with-http_perl_module=dynamic --with-http_random_index_module --with-http_secure_link_module --with-http_sub_module --with-http_xslt_module=dynamic --with-mail=dynamic --with-mail_ssl_module --with-stream=dynamic --with-stream_ssl_module --with-stream_ssl_preread_module --add-dynamic-module=../nginx-upload-module-master

make modules

If you're missing any dependencies, these configure and make commands might fail. Make sure you install any dependencies that are missing and try again.

Installing the compiled module

The last step is just to move the compiled module to a place that NGINX can find and tell NGINX to load it. We'll move the compiled dynamic module to /usr/lib/nginx/modules where the other modules live and then create a config file telling NGINX to load that module.

sudo mv objs/ngx_http_upload_module.so /usr/lib/nginx/modules

echo 'load_module /usr/lib/nginx/modules/ngx_http_upload_module.so;' | sudo tee /etc/nginx/modules-enabled/50-mod-http-upload.conf

Testing the module

Last but not least we want to confirm everything is working. A quick check is we can run the following command to verify our config. If everything went well, you should get the following output.

sudo nginx -t
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful

And for the real test, you can add the module's lines to your nginx server block, restart nginx, and make sure they actually work.

Conclusion

Dynamic modules are awesome. While they might be a bit harder to compile, we don't have to worry about recompiling ALL of NGINX anymore if we want to use some new plugin. Hopefully this guide helps you get modules compiled for your OS so you can continue using your repository-provided copy of NGINX.

Continue reading →

GoRails: Question And Answer #1

If you have more questions you'd like to hear answered, ask them below in the comments!

Continue reading →

Announcing New Features And Subscriptions Plans

With the holiday season just beginning, I'm proud to announce that GoRails gets a fresh coat of paint and a bunch of new features to make learning Ruby on Rails that much better. 🎉

Here's some of the awesome new stuff you can check out right now:

New feature: Series


GoRails Series

I'm now organizing videos in Series so you can dive deep into specific topics. After publishing over 150 different episodes, there's a lot of content on GoRails and it can be hard to dive into higher level topics, so that's what Series are here to solve. I've already grouped some of the existing episodes into series for easier watching, and I'll be releasing brand new series on APIs, Testing, React, Vue, and more coming very, very soon.

Keep an eye on the GoRails Series page for new releases!

New Feature: Watch Later

Watch Later

When you're browsing GoRails, sometimes a few episodes catch your eye and you want to make sure to mark those down to watch later. Now you can! Hit the "Watch Later" button and the episodes will be added to your queue on your Dashboard.

New Feature: Mark as Complete

Mark as complete

It's hard to keep track of which episodes you've watched and which you haven't, so I've added the ability for you to mark episodes as complete.

Upgraded: Forums


GoRails forums

The forums have been a fantastic place to ask questions and get answers. Like Github, I've added the ability to add reactions to posts so you can easily add your thoughts to a conversation.

I've also added subscriber badges and reactions to the forum. You earn a new badge for each milestone you reach of 1, 3, 6, 12, and 24 months.

New Subscription Plans

Along with all the new features, I'm updating our pricing. I'll be investing everything back into GoRails to expand the topics into more things like Vue.js, iOS / Android with Rails backends, testing, and more. I'm also planning on expanding our community and improving support to make GoRails an even better place to get advice on how to build great web apps.

GoRails will be going up to $19/mo for all new subscribers. As a subscriber, you'll get access to all lessons on GoRails and support the subscribers-only forum and private Slack group.

If you haven't subscribed yet, GoRails is on sale for $9/mo up until Cyber Monday (November 28, 2016). Be sure to subscribe to get the discount before it ends!

If you're currently subscribed to GoRails, don't worry! You'll be grandfathered in to your current plan as long as you're subscribed. That's my way of saying thanks for all the support you've given me over the years. 💕

Thank you

Almost 3 years ago I started working on GoRails fulltime. Today, there are over 10,000 people using GoRails. It's come an amazing way because of you. Thanks to your feedback, requests, and questions I've been able to make GoRails a much more useful resource for learning Ruby on Rails.

I believe you should be able to build any web app you can imagine, and have the resources to look up how to implement the features you'll need for it. We've accomplished some of this together but have a lot more to do. These changes are a big step in the direction of helping anyone become an incredibly good developer and I hope you're as excited about the future as I am! As always, I'm excited to hear your feedback.

yay

Continue reading →

Using Named Scopes Across Models with ActiveRecord#Merge

 


After years of working with ActiveRecord and watching it change so much, it is exciting to find new features you didn't know about. The one I discovered this week is ActiveRecord#merge. It is one of the most underused methods in ActiveRecord, due in part, to the name. It isn't necessarily clear what they mean by "merge" but it's simply a way of using a named scope on a joined model.

Say we have two models that are associated and one of them has a scope:

class Author < ActiveRecord::Base
  has_many :books
end
class Book < ActiveRecord::Base
  belongs_to :author

  scope :available, ->{ where(available: true) }
end

Let's say we want to join the tables to find all Authors who have books that are available. Without ActiveRecord#merge, we have to make this query:

Author.joins(:books).where("books.available = ?", true)
SELECT "authors".* FROM "authors" INNER JOIN "books" ON "books"."author_id" = "authors"."id" WHERE "books"."available" = 't'

But with ActiveRecord#merge, this becomes a whole lot cleaner and we don't duplicate the available scope:

Author.joins(:books).merge(Book.available)
SELECT "authors".* FROM "authors" INNER JOIN "books" ON "books"."author_id" = "authors"."id" WHERE "books"."available" = 't'

As you can see, the resulting SQL queries are exactly the same. ActiveRecord#merge is a great way to reduce the duplication in your code to continue relying on the named scopes you define in your models. I really want to see more people using this so please share this around!

Want to learn more about Rails and become a great programmer?

Check out the Ruby on Rails Screencasts for more awesome learning material like this.

Continue reading →

SimpleCalendar 1.1 released!

Having spent a significant time over the years playing with various calendar gems, I felt they were all lacking in different ways. Some were overbearing providing you CSS stylesheets when all I wanted was a plain calendar. Others were a bit too involved, there was a lot of work on my end and all I wanted to do was render a simple table for the calendar.

Calendars also aren't necessarily a month anyways. Sometimes you want to see 6 weeks at a time. Sometimes 2 weeks. Sometimes you only want 4 days. But none of the gems seemed to care.

Seeing all this, I decided to make my own: github.com/excid3/simple_calendar

At the simplest version, simple_calendar renders a table for your calendar.

You can make a month calendar:

<%= month_calendar do |date| %>
<%= date %>
<% end %>

You can make a 2 week calendar:

<%= week_calendar number_of_weeks: 2 do |date| %>
<%= date %>
<% end %>

You can make a 4 day agenda calendar:

<%= calendar number_of_days: 4 do |date| %>
<%= date %>
<% end %>

The calendar just generates some basic HTML for you that you are free to work with, complete with navigation links and a header (if you want it).

<div>
  <a href="/events?start_date=2014-05-13">«</a>
  <a href="/events?start_date=2014-05-18">»</a>
</div>
<table class="table table-bordered">
  <tbody>
    <tr>
      <td class="day today current-month wday-3" current="2014-05-14" start="2014-05-14">
        2014-05-14
      </td>
      <td class="day future current-month wday-4" current="2014-05-15" start="2014-05-14">
        2014-05-15
      </td>
      <td class="day future current-month wday-5" current="2014-05-16" start="2014-05-14">
        2014-05-16
      </td>
      <td class="day future current-month wday-6" current="2014-05-17" start="2014-05-14">
        2014-05-17
      </td>
    </tr>
  </tbody>
</table>

These are fine and dandy, but what is a calendar without events? We've got that too.

Because we need to know what attribute on your model is the event's start time, we have a simple include in your model that lets you define it:

class Event < ActiveRecord::Base
extend SimpleCalendar
has_calendar attribute: :start_time
end

You define which attribute to use in your model and then simply pass them into your view to have them all sorted out by day.

<%= month_calendar events: Event.all do |date, events| %>
<%= date %>
<% events.each do |event %>
<div><%= event.name %></div>
<% end %>
<% end %>

This will yield the date and all the events sorted by start time for the given date to your code. Do with them however you please, it is up to you.

I'm pleased with the end result currently and look forward to improving it more in the future. If you have any suggestions or improvements to make, make a Github issue and let's make the easiest calendar for Rails we can!

Try it out at https://github.com/excid3/simple_calendar

Continue reading →